RecorderUI: initial implementation
This commit is contained in:
parent
f10d380d9a
commit
582c99a156
|
@ -23,9 +23,14 @@
|
|||
#define __gtk_ardour_group_tabs_h__
|
||||
|
||||
#include <gtkmm/menu.h>
|
||||
#include "editor_component.h"
|
||||
|
||||
#include "ardour/session_handle.h"
|
||||
#include "ardour/types.h"
|
||||
|
||||
#include "gtkmm2ext/cairo_widget.h"
|
||||
|
||||
#include "editor_component.h"
|
||||
|
||||
namespace ARDOUR {
|
||||
class Session;
|
||||
class RouteGroup;
|
||||
|
|
|
@ -0,0 +1,591 @@
|
|||
/*
|
||||
* Copyright (C) 2021 Robin Gareus <robin@gareus.org>
|
||||
*
|
||||
* 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.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifdef WAF_BUILD
|
||||
#include "gtk2ardour-config.h"
|
||||
#endif
|
||||
|
||||
#include "ardour/dB.h"
|
||||
#include "ardour/logmeter.h"
|
||||
#include "ardour/parameter_descriptor.h"
|
||||
#include "ardour/port_manager.h"
|
||||
|
||||
#include "gtkmm2ext/utils.h"
|
||||
|
||||
#include "widgets/fastmeter.h"
|
||||
#include "widgets/tooltips.h"
|
||||
|
||||
#include "input_port_monitor.h"
|
||||
#include "ui_config.h"
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace ArdourWidgets;
|
||||
|
||||
#define PX_SCALE(px) std::max ((float)px, rintf ((float)px* UIConfiguration::instance ().get_ui_scale ()))
|
||||
|
||||
InputPortMonitor::InputPortMonitor (ARDOUR::DataType dt, samplecnt_t sample_rate, Orientation o)
|
||||
: _dt (dt)
|
||||
, _audio_meter (0)
|
||||
, _audio_scope (0)
|
||||
, _midi_meter (0)
|
||||
, _midi_monitor (0)
|
||||
{
|
||||
if (o == Vertical) {
|
||||
_box = new Gtk::HBox;
|
||||
} else {
|
||||
_box = new Gtk::VBox;
|
||||
}
|
||||
|
||||
if (_dt == DataType::AUDIO) {
|
||||
_audio_meter = new FastMeter (
|
||||
(uint32_t)floor (UIConfiguration::instance ().get_meter_hold ()),
|
||||
18,
|
||||
o == Vertical ? FastMeter::Vertical : FastMeter::Horizontal,
|
||||
PX_SCALE (200),
|
||||
UIConfiguration::instance ().color ("meter color0"),
|
||||
UIConfiguration::instance ().color ("meter color1"),
|
||||
UIConfiguration::instance ().color ("meter color2"),
|
||||
UIConfiguration::instance ().color ("meter color3"),
|
||||
UIConfiguration::instance ().color ("meter color4"),
|
||||
UIConfiguration::instance ().color ("meter color5"),
|
||||
UIConfiguration::instance ().color ("meter color6"),
|
||||
UIConfiguration::instance ().color ("meter color7"),
|
||||
UIConfiguration::instance ().color ("meter color8"),
|
||||
UIConfiguration::instance ().color ("meter color9"),
|
||||
UIConfiguration::instance ().color ("meter background bottom"),
|
||||
UIConfiguration::instance ().color ("meter background top"),
|
||||
0x991122ff, // red highlight gradient Bot
|
||||
0x551111ff, // red highlight gradient Top
|
||||
(115.0 * log_meter0dB (-18)),
|
||||
89.125, // 115.0 * log_meter0dB(-9);
|
||||
106.375, // 115.0 * log_meter0dB(-3);
|
||||
115.0, // 115.0 * log_meter0dB(0);
|
||||
(UIConfiguration::instance ().get_meter_style_led () ? 3 : 1));
|
||||
|
||||
_audio_scope = new InputScope (sample_rate, PX_SCALE (200), 25, o);
|
||||
|
||||
_audio_meter->show ();
|
||||
_audio_scope->show ();
|
||||
|
||||
ArdourWidgets::set_tooltip (_audio_scope, _("5 second history waveform"));
|
||||
|
||||
_box->pack_start (*_audio_meter, false, false);
|
||||
_box->pack_start (*_audio_scope, true, true, 1);
|
||||
|
||||
} else if (_dt == DataType::MIDI) {
|
||||
_midi_meter = new EventMeter (o);
|
||||
_midi_monitor = new EventMonitor (o);
|
||||
_midi_meter->show ();
|
||||
_midi_monitor->show ();
|
||||
|
||||
ArdourWidgets::set_tooltip (_midi_meter, _("Highlight incoming MIDI data per MIDI channel"));
|
||||
ArdourWidgets::set_tooltip (_midi_monitor, _("Display most recently received MIDI messages"));
|
||||
|
||||
_box->pack_start (*_midi_meter, false, false);
|
||||
_box->pack_start (*_midi_monitor, true, false, 1);
|
||||
}
|
||||
add (*_box);
|
||||
_box->show ();
|
||||
}
|
||||
|
||||
InputPortMonitor::~InputPortMonitor ()
|
||||
{
|
||||
delete _audio_meter;
|
||||
delete _audio_scope;
|
||||
delete _midi_meter;
|
||||
delete _midi_monitor;
|
||||
delete _box;
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::clear ()
|
||||
{
|
||||
if (_audio_meter) {
|
||||
_audio_meter->clear ();
|
||||
}
|
||||
if (_audio_scope) {
|
||||
_audio_scope->clear ();
|
||||
}
|
||||
if (_midi_meter) {
|
||||
_midi_meter->clear ();
|
||||
}
|
||||
if (_midi_monitor) {
|
||||
_midi_monitor->clear ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::update (float l, float p)
|
||||
{
|
||||
assert (_dt == DataType::AUDIO && _audio_meter);
|
||||
_audio_meter->set (log_meter0dB (l), log_meter0dB (p));
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::update (ARDOUR::CircularSampleBuffer& csb)
|
||||
{
|
||||
assert (_dt == DataType::AUDIO && _audio_scope);
|
||||
_audio_scope->update (csb);
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::update (float const* v)
|
||||
{
|
||||
assert (_dt == DataType::MIDI && _midi_meter);
|
||||
_midi_meter->update (v);
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::update (ARDOUR::CircularEventBuffer& ceb)
|
||||
{
|
||||
assert (_dt == DataType::MIDI && _midi_monitor);
|
||||
_midi_monitor->update (ceb);
|
||||
}
|
||||
|
||||
/* ****************************************************************************/
|
||||
|
||||
InputPortMonitor::InputScope::InputScope (samplecnt_t rate, int l, int g, Orientation o)
|
||||
: _pos (0)
|
||||
, _rate (rate)
|
||||
, _min_length (l)
|
||||
, _min_gauge (g)
|
||||
, _orientation (o)
|
||||
{
|
||||
_surface = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, l, g);
|
||||
use_image_surface (false); /* we already use a surface */
|
||||
|
||||
UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &InputScope::parameter_changed));
|
||||
parameter_changed ("waveform-clip-level");
|
||||
parameter_changed ("show-waveform-clipping");
|
||||
parameter_changed ("waveform-scale");
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::InputScope::parameter_changed (std::string const& p)
|
||||
{
|
||||
if (p == "waveform-clip-level") {
|
||||
_clip_level = dB_to_coefficient (UIConfiguration::instance ().get_waveform_clip_level ());
|
||||
} else if (p == "show-waveform-clipping") {
|
||||
_show_clip = UIConfiguration::instance ().get_show_waveform_clipping ();
|
||||
} else if (p == "waveform-scale") {
|
||||
_logscale = UIConfiguration::instance ().get_waveform_scale () == Logarithmic;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::InputScope::on_size_request (Gtk::Requisition* req)
|
||||
{
|
||||
if (_orientation == Horizontal) {
|
||||
req->width = 2 + _min_length;
|
||||
req->height = 2 + _min_gauge;
|
||||
} else {
|
||||
req->width = 2 + _min_gauge;
|
||||
req->height = 2 + _min_length;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::InputScope::on_size_allocate (Gtk::Allocation& a)
|
||||
{
|
||||
CairoWidget::on_size_allocate (a);
|
||||
|
||||
int w, h;
|
||||
if (_orientation == Horizontal) {
|
||||
w = 2 + _surface->get_width ();
|
||||
h = 2 + _surface->get_height ();
|
||||
} else {
|
||||
w = 2 + _surface->get_height ();
|
||||
h = 2 + _surface->get_width ();
|
||||
}
|
||||
|
||||
if (a.get_width () != w || a.get_height () != h) {
|
||||
_surface = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, a.get_width () - 2, a.get_height () - 2);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::InputScope::clear ()
|
||||
{
|
||||
int w = _surface->get_width ();
|
||||
int h = _surface->get_height ();
|
||||
|
||||
Cairo::RefPtr<Cairo::Context> cr= Cairo::Context::create (_surface);
|
||||
cr->rectangle (0, 0, w, h);
|
||||
cr->set_operator (Cairo::OPERATOR_SOURCE);
|
||||
cr->set_source_rgba (0, 0, 0, 0);
|
||||
cr->fill ();
|
||||
_pos = 0;
|
||||
set_dirty ();
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::InputScope::update (CircularSampleBuffer& csb)
|
||||
{
|
||||
int l = _orientation == Horizontal ? _surface->get_width () : _surface->get_height ();
|
||||
int g = _orientation == Horizontal ? _surface->get_height () : _surface->get_width ();
|
||||
|
||||
double g2 = g / 2.0;
|
||||
|
||||
int spp = 5.0 /*sec*/ * _rate / l; // samples / pixel
|
||||
Cairo::RefPtr<Cairo::Context> cr;
|
||||
|
||||
bool have_data = false;
|
||||
float minf, maxf;
|
||||
|
||||
while (csb.read (minf, maxf, spp)) {
|
||||
if (!have_data) {
|
||||
have_data = true;
|
||||
cr = Cairo::Context::create (_surface);
|
||||
}
|
||||
|
||||
/* see also ExportReport::draw_waveform */
|
||||
if (_orientation == Horizontal) {
|
||||
cr->rectangle (_pos, 0, 1, g);
|
||||
} else {
|
||||
if (_pos-- == 0) {
|
||||
_pos = l - 1;
|
||||
}
|
||||
cr->rectangle (0, _pos, g, 1);
|
||||
}
|
||||
cr->set_operator (Cairo::OPERATOR_SOURCE);
|
||||
cr->set_source_rgba (0, 0, 0, 0);
|
||||
cr->fill ();
|
||||
|
||||
cr->set_operator (Cairo::OPERATOR_OVER);
|
||||
cr->set_line_width (1.0);
|
||||
|
||||
if (_show_clip && (maxf >= _clip_level || -minf >= _clip_level)) {
|
||||
Gtkmm2ext::set_source_rgba (cr, UIConfiguration::instance ().color ("clipped waveform"));
|
||||
} else {
|
||||
Gtkmm2ext::set_source_rgba (cr, UIConfiguration::instance ().color ("waveform fill"));
|
||||
}
|
||||
|
||||
if (_logscale) {
|
||||
if (maxf > 0) {
|
||||
maxf = alt_log_meter (fast_coefficient_to_dB (maxf));
|
||||
} else {
|
||||
maxf = -alt_log_meter (fast_coefficient_to_dB (-maxf));
|
||||
}
|
||||
if (minf > 0) {
|
||||
minf = alt_log_meter (fast_coefficient_to_dB (minf));
|
||||
} else {
|
||||
minf = -alt_log_meter (fast_coefficient_to_dB (-minf));
|
||||
}
|
||||
}
|
||||
|
||||
if (_orientation == Horizontal) {
|
||||
cr->move_to (_pos + .5, g2 - g2 * maxf);
|
||||
cr->line_to (_pos + .5, g2 - g2 * minf);
|
||||
cr->stroke ();
|
||||
if (++_pos >= l) {
|
||||
_pos = 0;
|
||||
}
|
||||
} else {
|
||||
cr->move_to (g2 + g2 * minf, _pos + .5);
|
||||
cr->line_to (g2 + g2 * maxf, _pos + .5);
|
||||
cr->stroke ();
|
||||
}
|
||||
}
|
||||
|
||||
if (have_data) {
|
||||
_surface->flush ();
|
||||
set_dirty ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::InputScope::render (Cairo::RefPtr<Cairo::Context> const& cr, cairo_rectangle_t* r)
|
||||
{
|
||||
cr->rectangle (r->x, r->y, r->width, r->height);
|
||||
cr->clip ();
|
||||
cr->set_operator (Cairo::OPERATOR_OVER);
|
||||
|
||||
int w = _surface->get_width ();
|
||||
int h = _surface->get_height ();
|
||||
|
||||
cr->save ();
|
||||
cr->translate (1, 1);
|
||||
cr->rectangle (0, 0, w, h);
|
||||
cr->clip ();
|
||||
|
||||
if (_orientation == Vertical) {
|
||||
cr->set_source (_surface, 0, 0.0 - _pos);
|
||||
cr->paint ();
|
||||
cr->set_source (_surface, 0, h - _pos);
|
||||
cr->paint ();
|
||||
|
||||
double g2 = .5 * w;
|
||||
cr->move_to (g2, 0);
|
||||
cr->line_to (g2, h);
|
||||
|
||||
} else {
|
||||
cr->set_source (_surface, 0.0 - _pos, 0);
|
||||
cr->paint ();
|
||||
cr->set_source (_surface, w - _pos, 0);
|
||||
cr->paint ();
|
||||
|
||||
double g2 = .5 * h;
|
||||
cr->move_to (0, g2);
|
||||
cr->line_to (w, g2);
|
||||
}
|
||||
|
||||
/* zero line */
|
||||
cr->set_line_width (1.0);
|
||||
Gtkmm2ext::set_source_rgb_a (cr, UIConfiguration::instance ().color ("zero line"), .7);
|
||||
cr->stroke ();
|
||||
|
||||
/* black border - compare to FastMeter::horizontal_expose */
|
||||
cr->set_line_width (2.0);
|
||||
Gtkmm2ext::rounded_rectangle (cr, 0, 0, get_width (), get_height (), boxy_buttons () ? 0 : 2);
|
||||
cr->set_source_rgb (0, 0, 0); // black
|
||||
cr->stroke ();
|
||||
}
|
||||
|
||||
/* ****************************************************************************/
|
||||
|
||||
InputPortMonitor::EventMeter::EventMeter (Orientation o)
|
||||
: _orientation (o)
|
||||
{
|
||||
_layout = Pango::Layout::create (get_pango_context ());
|
||||
memset (_chn, 0, sizeof (_chn));
|
||||
|
||||
UIConfiguration::instance().DPIReset.connect (sigc::mem_fun (*this, &EventMeter::dpi_reset));
|
||||
dpi_reset ();
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::EventMeter::dpi_reset ()
|
||||
{
|
||||
_layout->set_font_description (UIConfiguration::instance ().get_SmallMonospaceFont ());
|
||||
_layout->set_text ("Cy5");
|
||||
_layout->get_pixel_size (_length, _extent);
|
||||
_extent += 2;
|
||||
_length += 2;
|
||||
queue_resize ();
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::EventMeter::on_size_request (Gtk::Requisition* req)
|
||||
{
|
||||
if (_orientation == Horizontal) {
|
||||
/* 90 deg CCW */
|
||||
req->width = _extent * 17 + 4;
|
||||
req->height = _length + 2;
|
||||
} else {
|
||||
req->width = _length + 2;
|
||||
req->height = _extent * 17 + 4;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::EventMeter::clear ()
|
||||
{
|
||||
memset (_chn, 0, sizeof (_chn));
|
||||
set_dirty ();
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::EventMeter::update (float const* v)
|
||||
{
|
||||
if (memcmp (_chn, v, sizeof (_chn))) {
|
||||
memcpy (_chn, v, sizeof (_chn));
|
||||
set_dirty ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::EventMeter::render (Cairo::RefPtr<Cairo::Context> const& cr, cairo_rectangle_t* r)
|
||||
{
|
||||
cr->rectangle (r->x, r->y, r->width, r->height);
|
||||
cr->clip ();
|
||||
|
||||
double bg_r, bg_g, bg_b, bg_a;
|
||||
double fg_r, fg_g, fg_b, fg_a;
|
||||
|
||||
Gtkmm2ext::color_to_rgba (UIConfiguration::instance ().color ("meter bar"), bg_r, bg_g, bg_b, bg_a);
|
||||
Gtkmm2ext::color_to_rgba (UIConfiguration::instance ().color ("midi meter 56"), fg_r, fg_g, fg_b, fg_a);
|
||||
|
||||
fg_r -= bg_r;
|
||||
fg_g -= bg_g;
|
||||
fg_b -= bg_b;
|
||||
|
||||
cr->set_operator (Cairo::OPERATOR_OVER);
|
||||
cr->set_line_width (1.0);
|
||||
|
||||
for (uint32_t i = 0; i < 17; ++i) {
|
||||
float off = 1.5 + _extent * i;
|
||||
|
||||
if (_orientation == Horizontal) {
|
||||
Gtkmm2ext::rounded_rectangle (cr, off, 0.5, _extent, _length, boxy_buttons () ? 0 : 2);
|
||||
} else {
|
||||
Gtkmm2ext::rounded_rectangle (cr, 0.5, off, _length, _extent, boxy_buttons () ? 0 : 2);
|
||||
}
|
||||
|
||||
cr->set_source_rgba (bg_r + _chn[i] * fg_r, bg_g + _chn[i] * fg_g, bg_b + _chn[i] * fg_b, .9);
|
||||
cr->fill_preserve ();
|
||||
Gtkmm2ext::set_source_rgba (cr, UIConfiguration::instance ().color ("border color"));
|
||||
cr->stroke ();
|
||||
|
||||
if (i < 16) {
|
||||
_layout->set_text (PBD::to_string (i + 1));
|
||||
} else {
|
||||
_layout->set_text ("SyS");
|
||||
}
|
||||
|
||||
int l, x;
|
||||
_layout->get_pixel_size (l, x);
|
||||
Gtkmm2ext::set_source_rgba (cr, UIConfiguration::instance ().color ("neutral:foreground2"));
|
||||
|
||||
if (_orientation == Horizontal) {
|
||||
cr->save ();
|
||||
cr->move_to (off + .5 * (_extent - x), .5 + .5 * (_length + l));
|
||||
cr->rotate (M_PI / -2.0);
|
||||
_layout->show_in_cairo_context (cr);
|
||||
cr->restore ();
|
||||
} else {
|
||||
cr->move_to (0.5 + .5 * (_length - l), off + .5 * (_extent - x - 2));
|
||||
_layout->show_in_cairo_context (cr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ****************************************************************************/
|
||||
|
||||
InputPortMonitor::EventMonitor::EventMonitor (Orientation o)
|
||||
: _orientation (o)
|
||||
{
|
||||
_layout = Pango::Layout::create (get_pango_context ());
|
||||
|
||||
UIConfiguration::instance().DPIReset.connect (sigc::mem_fun (*this, &EventMonitor::dpi_reset));
|
||||
dpi_reset ();
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::EventMonitor::dpi_reset ()
|
||||
{
|
||||
_layout->set_font_description (UIConfiguration::instance ().get_SmallMonospaceFont ());
|
||||
_layout->set_text ("OffC#-1"); // 7 chars
|
||||
_layout->get_pixel_size (_width, _height);
|
||||
_width += 2;
|
||||
_height += 2;
|
||||
queue_resize ();
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::EventMonitor::on_size_request (Gtk::Requisition* req)
|
||||
{
|
||||
if (_orientation == Horizontal) {
|
||||
req->width = PX_SCALE (200);
|
||||
req->height = _height;
|
||||
} else {
|
||||
req->width = _width;
|
||||
req->height = 8 * _height;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::EventMonitor::clear ()
|
||||
{
|
||||
_l.clear ();
|
||||
set_dirty ();
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::EventMonitor::update (CircularEventBuffer& ceb)
|
||||
{
|
||||
if (ceb.read (_l)) {
|
||||
set_dirty ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
InputPortMonitor::EventMonitor::render (Cairo::RefPtr<Cairo::Context> const& cr, cairo_rectangle_t* r)
|
||||
{
|
||||
int ww = get_width () - 12;
|
||||
int hh = 2;
|
||||
|
||||
for (CircularEventBuffer::EventList::const_iterator i = _l.begin (); i != _l.end (); ++i) {
|
||||
if (i->data[0] == 0) {
|
||||
break;
|
||||
}
|
||||
char tmp[32];
|
||||
switch (i->data[0] & 0xf0) {
|
||||
case MIDI_CMD_NOTE_OFF:
|
||||
sprintf (tmp, "Off%4s", ParameterDescriptor::midi_note_name (i->data[1]).c_str ());
|
||||
break;
|
||||
case MIDI_CMD_NOTE_ON:
|
||||
sprintf (tmp, "On %4s", ParameterDescriptor::midi_note_name (i->data[1]).c_str ());
|
||||
break;
|
||||
case MIDI_CMD_NOTE_PRESSURE:
|
||||
sprintf (tmp, "KP %4s", ParameterDescriptor::midi_note_name (i->data[1]).c_str ());
|
||||
break;
|
||||
case MIDI_CMD_CONTROL:
|
||||
sprintf (tmp, "CC%02x %02x", i->data[1], i->data[2]);
|
||||
break;
|
||||
case MIDI_CMD_PGM_CHANGE:
|
||||
sprintf (tmp, "PC %3d ", i->data[1]);
|
||||
break;
|
||||
case MIDI_CMD_CHANNEL_PRESSURE:
|
||||
sprintf (tmp, "CP %02x ", i->data[1]);
|
||||
break;
|
||||
case MIDI_CMD_BENDER:
|
||||
sprintf (tmp, "PB %04x", i->data[1] | i->data[2] << 7);
|
||||
break;
|
||||
case MIDI_CMD_COMMON_SYSEX:
|
||||
// TODO sub-type ?
|
||||
sprintf (tmp, " SysEx ");
|
||||
break;
|
||||
}
|
||||
|
||||
int w, h;
|
||||
_layout->set_text (tmp);
|
||||
_layout->get_pixel_size (w, h);
|
||||
|
||||
Gtkmm2ext::set_source_rgb_a (cr, UIConfiguration::instance ().color ("widget:bg"), .7);
|
||||
|
||||
if (_orientation == Horizontal) {
|
||||
Gtkmm2ext::rounded_rectangle (cr, ww - w - 1, 1, 2 + w, _height - 3, _height / 4.0);
|
||||
cr->fill ();
|
||||
|
||||
Gtkmm2ext::set_source_rgba (cr, UIConfiguration::instance ().color ("neutral:foreground2"));
|
||||
cr->move_to (ww - w, .5 * (_height - h));
|
||||
_layout->show_in_cairo_context (cr);
|
||||
|
||||
ww -= w + 12;
|
||||
|
||||
if (ww < w) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
Gtkmm2ext::rounded_rectangle (cr, 1, hh + 1, _width, _height - 3, _height / 4.0);
|
||||
cr->fill ();
|
||||
|
||||
Gtkmm2ext::set_source_rgba (cr, UIConfiguration::instance ().color ("neutral:foreground2"));
|
||||
cr->move_to (.5 * (_width - w), hh);
|
||||
_layout->show_in_cairo_context (cr);
|
||||
|
||||
hh += _height;
|
||||
|
||||
if (hh + h >= get_height ()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright (C) 2021 Robin Gareus <robin@gareus.org>
|
||||
*
|
||||
* 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.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef __gtk_ardour_input_port_monitor_h__
|
||||
#define __gtk_ardour_input_port_monitor_h__
|
||||
|
||||
#include <gtkmm/box.h>
|
||||
|
||||
#include "gtkmm2ext/cairo_widget.h"
|
||||
|
||||
#include "ardour/circular_buffer.h"
|
||||
#include "ardour/session_handle.h"
|
||||
|
||||
namespace ArdourWidgets
|
||||
{
|
||||
class FastMeter;
|
||||
}
|
||||
|
||||
class InputPortMonitor : public Gtk::EventBox
|
||||
{
|
||||
public:
|
||||
enum Orientation {
|
||||
Vertical,
|
||||
Horizontal
|
||||
};
|
||||
|
||||
InputPortMonitor (ARDOUR::DataType, ARDOUR::samplecnt_t, Orientation);
|
||||
~InputPortMonitor ();
|
||||
|
||||
void clear ();
|
||||
void update (float, float); // FastMeter
|
||||
void update (float const*); // EventMeter
|
||||
void update (ARDOUR::CircularSampleBuffer&); // InputScope
|
||||
void update (ARDOUR::CircularEventBuffer&); // EventMonitor
|
||||
|
||||
private:
|
||||
class InputScope : public CairoWidget
|
||||
{
|
||||
public:
|
||||
InputScope (ARDOUR::samplecnt_t, int length , int gauge, Orientation);
|
||||
void update (ARDOUR::CircularSampleBuffer&);
|
||||
void clear ();
|
||||
|
||||
protected:
|
||||
void render (Cairo::RefPtr<Cairo::Context> const&, cairo_rectangle_t*);
|
||||
void on_size_request (Gtk::Requisition*);
|
||||
void on_size_allocate (Gtk::Allocation&);
|
||||
|
||||
private:
|
||||
void parameter_changed (std::string const&);
|
||||
|
||||
int _pos;
|
||||
ARDOUR::samplecnt_t _rate;
|
||||
int _min_length;
|
||||
int _min_gauge;
|
||||
Orientation _orientation;
|
||||
float _clip_level;
|
||||
bool _show_clip;
|
||||
bool _logscale;
|
||||
|
||||
Cairo::RefPtr<Cairo::ImageSurface> _surface;
|
||||
};
|
||||
|
||||
class EventMeter : public CairoWidget
|
||||
{
|
||||
public:
|
||||
EventMeter (Orientation);
|
||||
void update (float const*);
|
||||
void clear ();
|
||||
|
||||
protected:
|
||||
void render (Cairo::RefPtr<Cairo::Context> const&, cairo_rectangle_t*);
|
||||
void on_size_request (Gtk::Requisition*);
|
||||
|
||||
private:
|
||||
void dpi_reset ();
|
||||
|
||||
Glib::RefPtr<Pango::Layout> _layout;
|
||||
float _chn[17];
|
||||
int _length;
|
||||
int _extent;
|
||||
Orientation _orientation;
|
||||
};
|
||||
|
||||
class EventMonitor : public CairoWidget
|
||||
{
|
||||
public:
|
||||
EventMonitor (Orientation);
|
||||
void update (ARDOUR::CircularEventBuffer&);
|
||||
void clear ();
|
||||
|
||||
protected:
|
||||
void render (Cairo::RefPtr<Cairo::Context> const&, cairo_rectangle_t*);
|
||||
void on_size_request (Gtk::Requisition*);
|
||||
|
||||
private:
|
||||
void dpi_reset ();
|
||||
|
||||
ARDOUR::CircularEventBuffer::EventList _l;
|
||||
Glib::RefPtr<Pango::Layout> _layout;
|
||||
int _width;
|
||||
int _height;
|
||||
Orientation _orientation;
|
||||
};
|
||||
|
||||
Gtk::Box* _box;
|
||||
ARDOUR::DataType _dt;
|
||||
ArdourWidgets::FastMeter* _audio_meter;
|
||||
InputScope* _audio_scope;
|
||||
EventMeter* _midi_meter;
|
||||
EventMonitor* _midi_monitor;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -3312,6 +3312,21 @@ RCOptionEditor::RCOptionEditor ()
|
|||
sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_save_export_mixer_screenshot)
|
||||
));
|
||||
|
||||
add_option (S_("Preferences|Metering"), new OptionEditorHeading (_("Input Meter Layout")));
|
||||
|
||||
ComboOption<InputMeterLayout>* iml = new ComboOption<InputMeterLayout> (
|
||||
"input-meter-layout",
|
||||
_("Input Meter Layout"),
|
||||
sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::get_input_meter_layout),
|
||||
sigc::mem_fun (UIConfiguration::instance(), &UIConfiguration::set_input_meter_layout)
|
||||
);
|
||||
|
||||
iml->add (LayoutAutomatic, _("Automatic"));
|
||||
iml->add (LayoutHorizontal, _("Horizontal"));
|
||||
iml->add (LayoutVertical, _("Vertical"));
|
||||
|
||||
add_option (S_("Preferences|Metering"), iml);
|
||||
|
||||
/* TRANSPORT & Sync */
|
||||
|
||||
add_option (_("Transport"), new OptionEditorHeading (_("General")));
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright (C) 2020 Robin Gareus <robin@gareus.org>
|
||||
*
|
||||
* 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.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "recorder_group_tabs.h"
|
||||
#include "recorder_ui.h"
|
||||
#include "track_record_axis.h"
|
||||
#include "ui_config.h"
|
||||
|
||||
#ifdef WAF_BUILD
|
||||
#include "gtk2ardour-config.h"
|
||||
#endif
|
||||
|
||||
using namespace ARDOUR;
|
||||
|
||||
RecorderGroupTabs::RecorderGroupTabs (RecorderUI* parent)
|
||||
: _recorder (parent)
|
||||
{
|
||||
}
|
||||
|
||||
double
|
||||
RecorderGroupTabs::primary_coordinate (double, double y) const
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
double
|
||||
RecorderGroupTabs::extent () const
|
||||
{
|
||||
return get_height ();
|
||||
}
|
||||
|
||||
std::list<GroupTabs::Tab>
|
||||
RecorderGroupTabs::compute_tabs () const
|
||||
{
|
||||
std::list<Tab> tabs;
|
||||
|
||||
Tab tab;
|
||||
tab.from = 0;
|
||||
tab.group = 0;
|
||||
int32_t y = 0;
|
||||
|
||||
std::list<TrackRecordAxis*> recorders = _recorder->visible_recorders ();
|
||||
for (std::list<TrackRecordAxis*>::const_iterator i = recorders.begin (); i != recorders.end (); ++i) {
|
||||
if ((*i)->route ()->presentation_info ().hidden ()) { // marked_for_display ()
|
||||
continue;
|
||||
}
|
||||
|
||||
RouteGroup* g = (*i)->route_group ();
|
||||
|
||||
if (g != tab.group) {
|
||||
if (tab.group) {
|
||||
tab.to = y;
|
||||
tabs.push_back (tab);
|
||||
}
|
||||
|
||||
tab.from = y;
|
||||
tab.group = g;
|
||||
if (g) {
|
||||
tab.color = group_color (g);
|
||||
}
|
||||
}
|
||||
|
||||
y += (*i)->get_height ();
|
||||
}
|
||||
|
||||
if (tab.group) {
|
||||
tab.to = y;
|
||||
tabs.push_back (tab);
|
||||
}
|
||||
|
||||
return tabs;
|
||||
}
|
||||
|
||||
RouteList
|
||||
RecorderGroupTabs::routes_for_tab (Tab const* t) const
|
||||
{
|
||||
RouteList routes;
|
||||
int32_t y = 0;
|
||||
|
||||
std::list<TrackRecordAxis*> recorders = _recorder->visible_recorders ();
|
||||
for (std::list<TrackRecordAxis*>::const_iterator i = recorders.begin (); i != recorders.end (); ++i) {
|
||||
if (y >= t->to) {
|
||||
/* tab finishes before this track starts */
|
||||
break;
|
||||
}
|
||||
|
||||
double const h = y + (*i)->get_height () / 2;
|
||||
if (t->from < h && t->to > h) {
|
||||
routes.push_back ((*i)->route ());
|
||||
}
|
||||
y += (*i)->get_height ();
|
||||
}
|
||||
|
||||
return routes;
|
||||
}
|
||||
|
||||
void
|
||||
RecorderGroupTabs::draw_tab (cairo_t* cr, Tab const& tab)
|
||||
{
|
||||
double const arc_radius = get_width ();
|
||||
double r, g, b, a;
|
||||
|
||||
if (tab.group && tab.group->is_active ()) {
|
||||
Gtkmm2ext::color_to_rgba (tab.color, r, g, b, a);
|
||||
} else {
|
||||
Gtkmm2ext::color_to_rgba (UIConfiguration::instance ().color ("inactive group tab"), r, g, b, a);
|
||||
}
|
||||
|
||||
a = 1.0;
|
||||
|
||||
cairo_set_source_rgba (cr, r, g, b, a);
|
||||
cairo_move_to (cr, 0, tab.from + arc_radius);
|
||||
cairo_arc (cr, get_width (), tab.from + arc_radius, arc_radius, M_PI, 3 * M_PI / 2);
|
||||
cairo_line_to (cr, get_width (), tab.to);
|
||||
cairo_arc (cr, get_width (), tab.to - arc_radius, arc_radius, M_PI / 2, M_PI);
|
||||
cairo_line_to (cr, 0, tab.from + arc_radius);
|
||||
cairo_fill (cr);
|
||||
|
||||
if (tab.group && (tab.to - tab.from) > arc_radius) {
|
||||
int text_width, text_height;
|
||||
|
||||
Glib::RefPtr<Pango::Layout> layout;
|
||||
layout = Pango::Layout::create (get_pango_context ());
|
||||
layout->set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
|
||||
|
||||
layout->set_text (tab.group->name ());
|
||||
layout->set_width ((tab.to - tab.from - arc_radius) * PANGO_SCALE);
|
||||
layout->get_pixel_size (text_width, text_height);
|
||||
|
||||
cairo_move_to (cr, (get_width () - text_height) * .5, (text_width + tab.to + tab.from) * .5);
|
||||
|
||||
Gtkmm2ext::Color c = Gtkmm2ext::contrasting_text_color (Gtkmm2ext::rgba_to_color (r, g, b, a));
|
||||
Gtkmm2ext::color_to_rgba (c, r, g, b, a);
|
||||
cairo_set_source_rgb (cr, r, g, b);
|
||||
|
||||
cairo_save (cr);
|
||||
cairo_rotate (cr, M_PI * -.5);
|
||||
pango_cairo_show_layout (cr, layout->gobj ());
|
||||
cairo_restore (cr);
|
||||
}
|
||||
}
|
||||
|
||||
RouteList
|
||||
RecorderGroupTabs::selected_routes () const
|
||||
{
|
||||
RouteList rl;
|
||||
return rl;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2020 Robin Gareus <robin@gareus.org>
|
||||
*
|
||||
* 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.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef __gtk_ardour_recorder_group_tabs_h__
|
||||
#define __gtk_ardour_recorder_group_tabs_h__
|
||||
|
||||
#include "group_tabs.h"
|
||||
|
||||
class RecorderUI;
|
||||
|
||||
class RecorderGroupTabs : public GroupTabs
|
||||
{
|
||||
public:
|
||||
RecorderGroupTabs (RecorderUI*);
|
||||
|
||||
private:
|
||||
std::list<Tab> compute_tabs () const;
|
||||
void draw_tab (cairo_t*, Tab const&);
|
||||
ARDOUR::RouteList routes_for_tab (Tab const*) const;
|
||||
ARDOUR::RouteList selected_routes () const;
|
||||
double primary_coordinate (double, double) const;
|
||||
double extent () const;
|
||||
|
||||
RecorderUI* _recorder;
|
||||
};
|
||||
|
||||
#endif /* __gtk_ardour_recorder_ui_h__ */
|
File diff suppressed because it is too large
Load Diff
|
@ -19,12 +19,36 @@
|
|||
#ifndef __gtk_ardour_recorder_ui_h__
|
||||
#define __gtk_ardour_recorder_ui_h__
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#include <gtkmm/alignment.h>
|
||||
#include <gtkmm/box.h>
|
||||
#include <gtkmm/scrolledwindow.h>
|
||||
#include <gtkmm/sizegroup.h>
|
||||
#include <gtkmm/table.h>
|
||||
|
||||
#include "pbd/natsort.h"
|
||||
|
||||
#include "ardour/session_handle.h"
|
||||
#include "ardour/circular_buffer.h"
|
||||
#include "ardour/types.h"
|
||||
|
||||
#include "gtkmm2ext/bindings.h"
|
||||
#include "gtkmm2ext/cairo_widget.h"
|
||||
|
||||
#include "widgets/ardour_button.h"
|
||||
#include "widgets/ardour_spacer.h"
|
||||
#include "widgets/frame.h"
|
||||
#include "widgets/pane.h"
|
||||
#include "widgets/tabbable.h"
|
||||
|
||||
#include "input_port_monitor.h"
|
||||
|
||||
class TrackRecordAxis;
|
||||
class RecorderGroupTabs;
|
||||
|
||||
class RecorderUI : public ArdourWidgets::Tabbable, public ARDOUR::SessionHandlePtr, public PBD::ScopedConnectionList
|
||||
{
|
||||
public:
|
||||
|
@ -39,16 +63,181 @@ public:
|
|||
|
||||
Gtk::Window* use_own_window (bool and_fill_it);
|
||||
|
||||
void spill_port (std::string const&);
|
||||
void add_track (std::string const&);
|
||||
|
||||
private:
|
||||
void load_bindings ();
|
||||
void register_actions ();
|
||||
void update_title ();
|
||||
void session_going_away ();
|
||||
void parameter_changed (std::string const&);
|
||||
void presentation_info_changed (PBD::PropertyChange const&);
|
||||
void gui_extents_changed ();
|
||||
|
||||
Gtkmm2ext::Bindings* bindings;
|
||||
void start_updating ();
|
||||
void stop_updating ();
|
||||
bool update_meters ();
|
||||
void add_or_remove_io (ARDOUR::DataType, std::vector<std::string>, bool);
|
||||
void update_io_widget_labels ();
|
||||
|
||||
Gtk::VBox _content;
|
||||
void initial_track_display ();
|
||||
void add_routes (ARDOUR::RouteList&);
|
||||
void remove_route (TrackRecordAxis*);
|
||||
void update_rec_table_layout ();
|
||||
void update_spacer_width (Gtk::Allocation&, TrackRecordAxis*);
|
||||
|
||||
void set_connections (std::string const&);
|
||||
void port_connected_or_disconnected (std::string, std::string);
|
||||
void port_pretty_name_changed (std::string);
|
||||
|
||||
void meter_area_size_allocate (Gtk::Allocation&);
|
||||
void meter_area_size_request (GtkRequisition*);
|
||||
void meter_area_layout ();
|
||||
|
||||
bool scroller_button_release (GdkEventButton*);
|
||||
|
||||
void arm_all ();
|
||||
void arm_none ();
|
||||
void peak_reset ();
|
||||
|
||||
void update_sensitivity ();
|
||||
void update_recordstate ();
|
||||
void new_track_for_port (ARDOUR::DataType, std::string const&);
|
||||
|
||||
static int calc_columns (int child_width, int parent_width);
|
||||
|
||||
Gtkmm2ext::Bindings* bindings;
|
||||
Gtk::VBox _content;
|
||||
Gtk::HBox _toolbar;
|
||||
Gtk::Table _button_table;
|
||||
ArdourWidgets::VPane _pane;
|
||||
Gtk::ScrolledWindow _rec_scroller;
|
||||
Gtk::VBox _rec_container;
|
||||
Gtk::HBox _rec_groups;
|
||||
Gtk::VBox _rec_area;
|
||||
Gtk::ScrolledWindow _meter_scroller;
|
||||
Gtk::VBox _meter_area;
|
||||
Gtk::Table _meter_table;
|
||||
Gtk::EventBox _scroller_base;
|
||||
|
||||
ArdourWidgets::ArdourHSpacer _toolbar_sep;
|
||||
Gtk::Label _recs_label;
|
||||
ArdourWidgets::ArdourButton _btn_rec_all;
|
||||
ArdourWidgets::ArdourButton _btn_rec_none;
|
||||
ArdourWidgets::ArdourButton _btn_rec_forget;
|
||||
ArdourWidgets::ArdourButton _btn_peak_reset;
|
||||
ArdourWidgets::ArdourButton _monitor_in_button;
|
||||
ArdourWidgets::ArdourButton _monitor_disk_button;
|
||||
ArdourWidgets::ArdourButton _auto_input_button;
|
||||
Glib::RefPtr<Gtk::SizeGroup> _toolbar_button_height;
|
||||
Glib::RefPtr<Gtk::SizeGroup> _toolbar_recarm_width;
|
||||
Glib::RefPtr<Gtk::SizeGroup> _toolbar_monitoring_width;
|
||||
|
||||
int _meter_box_width;
|
||||
int _meter_area_cols;
|
||||
bool _vertical;
|
||||
|
||||
std::set<std::string> _spill_port_names;
|
||||
|
||||
sigc::connection _fast_screen_update_connection;
|
||||
sigc::connection _ruler_width_update_connection;
|
||||
PBD::ScopedConnectionList _engine_connections;
|
||||
|
||||
class RecRuler : public CairoWidget , public ARDOUR::SessionHandlePtr
|
||||
{
|
||||
public:
|
||||
RecRuler ();
|
||||
|
||||
void playhead_position_changed (ARDOUR::samplepos_t);
|
||||
void set_gui_extents (samplepos_t, samplepos_t);
|
||||
void set_right_edge (int);
|
||||
|
||||
protected:
|
||||
void render (Cairo::RefPtr<Cairo::Context> const&, cairo_rectangle_t*);
|
||||
void on_size_request (Gtk::Requisition*);
|
||||
bool on_button_press_event (GdkEventButton*);
|
||||
|
||||
private:
|
||||
Glib::RefPtr<Pango::Layout> _layout;
|
||||
int _time_width;
|
||||
int _time_height;
|
||||
int _width;
|
||||
ARDOUR::samplecnt_t _left;
|
||||
ARDOUR::samplecnt_t _right;
|
||||
};
|
||||
|
||||
class InputPort : public Gtk::EventBox
|
||||
{
|
||||
public:
|
||||
InputPort (std::string const&, ARDOUR::DataType, RecorderUI*, bool vertical = false);
|
||||
~InputPort ();
|
||||
|
||||
void set_frame_label (std::string const&);
|
||||
void set_connections (ARDOUR::WeakRouteList);
|
||||
void setup_name ();
|
||||
bool spill (bool);
|
||||
bool spilled () const;
|
||||
void update_rec_stat ();
|
||||
|
||||
ARDOUR::DataType data_type () const;
|
||||
std::string const& name () const;
|
||||
|
||||
void update (float, float); // FastMeter
|
||||
void update (float const*); // EventMeter
|
||||
void update (ARDOUR::CircularSampleBuffer&); // InputScope
|
||||
void update (ARDOUR::CircularEventBuffer&); // EventMonitor
|
||||
void clear ();
|
||||
|
||||
bool operator< (InputPort const& o) const {
|
||||
if (_dt == o._dt) {
|
||||
return PBD::naturally_less (_port_name.c_str (), o._port_name.c_str ());
|
||||
}
|
||||
return _dt < (uint32_t) o._dt;
|
||||
}
|
||||
|
||||
private:
|
||||
void rename_port ();
|
||||
|
||||
ARDOUR::DataType _dt;
|
||||
InputPortMonitor _monitor;
|
||||
Gtk::Alignment _alignment;
|
||||
ArdourWidgets::Frame _frame;
|
||||
ArdourWidgets::ArdourButton _spill_button;
|
||||
ArdourWidgets::ArdourButton _name_button;
|
||||
Gtk::Label _name_label;
|
||||
ArdourWidgets::ArdourButton _add_button;
|
||||
std::string _port_name;
|
||||
ARDOUR::WeakRouteList _connected_routes;
|
||||
|
||||
static bool _size_groups_initialized;
|
||||
static Glib::RefPtr<Gtk::SizeGroup> _name_size_group;
|
||||
static Glib::RefPtr<Gtk::SizeGroup> _spill_size_group;
|
||||
static Glib::RefPtr<Gtk::SizeGroup> _button_size_group;
|
||||
static Glib::RefPtr<Gtk::SizeGroup> _monitor_size_group;
|
||||
};
|
||||
|
||||
struct InputPortPtrSort {
|
||||
bool operator() (boost::shared_ptr<InputPort> const& a, boost::shared_ptr<InputPort> const& b) const {
|
||||
return *a < *b;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<std::string, boost::shared_ptr<InputPort> > InputPortMap;
|
||||
|
||||
RecRuler _ruler;
|
||||
Gtk::EventBox _space;
|
||||
Gtk::HBox _ruler_box;
|
||||
ArdourWidgets::ArdourHSpacer _ruler_sep;
|
||||
RecorderGroupTabs* _rec_group_tabs;
|
||||
|
||||
InputPortMap _input_ports;
|
||||
std::list<TrackRecordAxis*> _recorders;
|
||||
std::list<TrackRecordAxis*> _visible_recorders;
|
||||
|
||||
public:
|
||||
/* only for RecorderGroupTab */
|
||||
std::list<TrackRecordAxis*> visible_recorders () const;
|
||||
};
|
||||
|
||||
#endif /* __gtk_ardour_recorder_ui_h__ */
|
||||
|
|
|
@ -0,0 +1,703 @@
|
|||
/*
|
||||
* Copyright (C) 2020 Robin Gareus <robin@gareus.org>
|
||||
*
|
||||
* 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.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <list>
|
||||
|
||||
#include <sigc++/bind.h>
|
||||
|
||||
#include "pbd/unwind.h"
|
||||
|
||||
#include "ardour/logmeter.h"
|
||||
#include "ardour/meter.h"
|
||||
#include "ardour/playlist.h"
|
||||
#include "ardour/route.h"
|
||||
#include "ardour/route_group.h"
|
||||
#include "ardour/selection.h"
|
||||
#include "ardour/session.h"
|
||||
#include "ardour/track.h"
|
||||
|
||||
#include "ardour/audio_track.h"
|
||||
#include "ardour/midi_track.h"
|
||||
|
||||
#include "gtkmm2ext/colors.h"
|
||||
#include "gtkmm2ext/gtk_ui.h"
|
||||
#include "gtkmm2ext/keyboard.h"
|
||||
#include "gtkmm2ext/rgb_macros.h"
|
||||
#include "gtkmm2ext/utils.h"
|
||||
|
||||
#include "widgets/tooltips.h"
|
||||
|
||||
#include "ardour_window.h"
|
||||
#include "context_menu_helper.h"
|
||||
#include "editor_cursors.h"
|
||||
#include "group_tabs.h"
|
||||
#include "gui_thread.h"
|
||||
#include "level_meter.h"
|
||||
#include "meter_patterns.h"
|
||||
#include "public_editor.h"
|
||||
#include "route_group_menu.h"
|
||||
#include "timers.h"
|
||||
#include "ui_config.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "track_record_axis.h"
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace ArdourMeter;
|
||||
using namespace ArdourWidgets;
|
||||
using namespace ARDOUR_UI_UTILS;
|
||||
using namespace PBD;
|
||||
using namespace Gtk;
|
||||
using namespace Gtkmm2ext;
|
||||
using namespace std;
|
||||
|
||||
PBD::Signal1<void, TrackRecordAxis*> TrackRecordAxis::CatchDeletion;
|
||||
|
||||
#define PX_SCALE(pxmin, dflt) rint (std::max ((double)pxmin, (double)dflt* UIConfiguration::instance ().get_ui_scale ()))
|
||||
|
||||
bool TrackRecordAxis::_size_group_initialized = false;
|
||||
Glib::RefPtr<Gtk::SizeGroup> TrackRecordAxis::_track_number_size_group;
|
||||
|
||||
TrackRecordAxis::TrackRecordAxis (Session* s, boost::shared_ptr<ARDOUR::Route> rt)
|
||||
: SessionHandlePtr (s)
|
||||
, RouteUI (s)
|
||||
, _clear_meters (true)
|
||||
, _route_ops_menu (0)
|
||||
, _input_button (true)
|
||||
, _playlist_button (S_("RTAV|P"))
|
||||
, _vseparator (1.0)
|
||||
, _ctrls_button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH))
|
||||
, _monitor_ctrl_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH))
|
||||
, _track_summary (rt)
|
||||
{
|
||||
if (!_size_group_initialized) {
|
||||
_size_group_initialized = true;
|
||||
_track_number_size_group = Gtk::SizeGroup::create (Gtk::SIZE_GROUP_BOTH);
|
||||
}
|
||||
|
||||
RouteUI::set_route (rt);
|
||||
|
||||
_route->DropReferences.connect (_route_connections, invalidator (*this), boost::bind (&TrackRecordAxis::self_delete, this), gui_context ());
|
||||
|
||||
UI::instance ()->theme_changed.connect (sigc::mem_fun (*this, &TrackRecordAxis::on_theme_changed));
|
||||
UIConfiguration::instance ().ColorsChanged.connect (sigc::mem_fun (*this, &TrackRecordAxis::on_theme_changed));
|
||||
UIConfiguration::instance ().DPIReset.connect (sigc::mem_fun (*this, &TrackRecordAxis::on_theme_changed));
|
||||
UIConfiguration::instance ().ParameterChanged.connect (sigc::mem_fun (*this, &TrackRecordAxis::parameter_changed));
|
||||
|
||||
Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&TrackRecordAxis::parameter_changed, this, _1), gui_context ());
|
||||
s->config.ParameterChanged.connect (*this, invalidator (*this), ui_bind (&TrackRecordAxis::parameter_changed, this, _1), gui_context ());
|
||||
|
||||
PublicEditor::instance().playhead_cursor()->PositionChanged.connect (*this, invalidator (*this), boost::bind (&TrackSummary::playhead_position_changed, &_track_summary, _1), gui_context());
|
||||
|
||||
ResetAllPeakDisplays.connect (sigc::mem_fun (*this, &TrackRecordAxis::reset_peak_display));
|
||||
ResetRoutePeakDisplays.connect (sigc::mem_fun (*this, &TrackRecordAxis::reset_route_peak_display));
|
||||
ResetGroupPeakDisplays.connect (sigc::mem_fun (*this, &TrackRecordAxis::reset_group_peak_display));
|
||||
|
||||
_number_label.set_name ("tracknumber label");
|
||||
_number_label.set_elements ((ArdourButton::Element) (ArdourButton::Edge | ArdourButton::Body | ArdourButton::Text | ArdourButton::Inactive));
|
||||
_number_label.set_alignment (.5, .5);
|
||||
_number_label.set_fallthrough_to_parent (true);
|
||||
_number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &TrackRecordAxis::route_ops_click), false);
|
||||
|
||||
PropertyList* plist = new PropertyList();
|
||||
plist->add (ARDOUR::Properties::group_mute, true);
|
||||
plist->add (ARDOUR::Properties::group_solo, true);
|
||||
|
||||
_playlist_button.set_name ("route button");
|
||||
_playlist_button.signal_button_press_event().connect (sigc::mem_fun(*this, &TrackRecordAxis::playlist_click), false);
|
||||
|
||||
_level_meter = new LevelMeterVBox (s);
|
||||
_level_meter->set_meter (_route->shared_peak_meter ().get ());
|
||||
_level_meter->clear_meters ();
|
||||
_level_meter->setup_meters (120, 12);
|
||||
|
||||
name_label.set_name (X_("TrackNameEditor"));
|
||||
name_label.set_alignment (0.0, 0.5);
|
||||
name_label.set_width_chars (12);
|
||||
|
||||
_input_button.set_sizing_text ("Capture_8888");
|
||||
_input_button.set_route (rt, this);
|
||||
|
||||
parameter_changed ("editor-stereo-only-meters");
|
||||
parameter_changed ("time-axis-name-ellipsize-mode");
|
||||
|
||||
/* force the track header buttons into a boxy grid-shape */
|
||||
rec_enable_button->set_tweaks(ArdourButton::Tweaks(ArdourButton::TrackHeader | ArdourButton::ForceBoxy));
|
||||
monitor_disk_button->set_tweaks(ArdourButton::Tweaks(ArdourButton::ForceBoxy));
|
||||
monitor_input_button->set_tweaks(ArdourButton::Tweaks(ArdourButton::ForceBoxy));
|
||||
_playlist_button.set_tweaks(ArdourButton::Tweaks(ArdourButton::TrackHeader | ArdourButton::ForceBoxy));
|
||||
_input_button.set_tweaks(ArdourButton::Tweaks(ArdourButton::ForceBoxy));
|
||||
_number_label.set_tweaks(ArdourButton::Tweaks(ArdourButton::ForceBoxy | ArdourButton::ForceFlat));
|
||||
|
||||
_ctrls.attach (*rec_enable_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::FILL, 0, 0);
|
||||
_ctrls.attach (_input_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::FILL, 0, 0);
|
||||
_ctrls.attach (_playlist_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::FILL, 0, 0);
|
||||
_ctrls.attach (*monitor_input_button, 5, 6, 0, 1, Gtk::SHRINK, Gtk::FILL, 0, 0);
|
||||
_ctrls.attach (*monitor_disk_button, 6, 7, 0, 1, Gtk::SHRINK, Gtk::FILL, 0, 0);
|
||||
_ctrls.attach (*_level_meter, 7, 8, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
|
||||
_ctrls.attach (_number_label, 8, 9, 0, 1, Gtk::SHRINK, Gtk::FILL, 0, 0);
|
||||
_ctrls.attach (_vseparator, 9, 10, 0, 1, Gtk::SHRINK, Gtk::FILL, 0, 0);
|
||||
_ctrls.attach (_track_summary, 10, 11, 0, 1, Gtk::EXPAND|FILL, Gtk::FILL, 1, 0);
|
||||
|
||||
set_tooltip (*mute_button, _("Mute"));
|
||||
set_tooltip (*rec_enable_button, _("Record"));
|
||||
set_tooltip (_playlist_button, _("Playlist")); // playlist_tip ()
|
||||
|
||||
set_name_label ();
|
||||
update_sensitivity ();
|
||||
|
||||
_track_number_size_group->add_widget (_number_label);
|
||||
_ctrls_button_size_group->add_widget (*rec_enable_button);
|
||||
_ctrls_button_size_group->add_widget (*mute_button);
|
||||
_ctrls_button_size_group->add_widget (_playlist_button);
|
||||
_monitor_ctrl_size_group->add_widget (*monitor_input_button);
|
||||
_monitor_ctrl_size_group->add_widget (*monitor_disk_button);
|
||||
|
||||
pack_start (_ctrls, false, false);
|
||||
|
||||
rec_enable_button->show ();
|
||||
monitor_input_button->show ();
|
||||
monitor_disk_button->show ();
|
||||
mute_button->show ();
|
||||
_level_meter->show ();
|
||||
_playlist_button.show();
|
||||
_number_label.show ();
|
||||
name_label.show ();
|
||||
_input_button.show ();
|
||||
_track_summary.show ();
|
||||
_vseparator.show ();
|
||||
_ctrls.show ();
|
||||
}
|
||||
|
||||
TrackRecordAxis::~TrackRecordAxis ()
|
||||
{
|
||||
delete _level_meter;
|
||||
delete _route_ops_menu;
|
||||
CatchDeletion (this);
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::self_delete ()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::set_session (Session* s)
|
||||
{
|
||||
RouteUI::set_session (s);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
s->config.ParameterChanged.connect (*this, invalidator (*this), ui_bind (&TrackRecordAxis::parameter_changed, this, _1), gui_context ());
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::blink_rec_display (bool onoff)
|
||||
{
|
||||
RouteUI::blink_rec_display (onoff);
|
||||
}
|
||||
|
||||
std::string
|
||||
TrackRecordAxis::state_id () const
|
||||
{
|
||||
if (_route) {
|
||||
return string_compose ("recctrl %1", _route->id ().to_s ());
|
||||
} else {
|
||||
return string ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::set_button_names ()
|
||||
{
|
||||
mute_button->set_text (S_("Mute|M"));
|
||||
#if 0
|
||||
monitor_input_button->set_text (S_("MonitorInput|I"));
|
||||
monitor_disk_button->set_text (S_("MonitorDisk|D"));
|
||||
#else
|
||||
monitor_input_button->set_text (_("In"));
|
||||
monitor_disk_button->set_text (_("Disk"));
|
||||
#endif
|
||||
|
||||
/* Solo/Listen is N/A */
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::route_property_changed (const PropertyChange& what_changed)
|
||||
{
|
||||
if (!what_changed.contains (ARDOUR::Properties::name)) {
|
||||
return;
|
||||
}
|
||||
ENSURE_GUI_THREAD (*this, &TrackRecordAxis::route_property_changed, what_changed);
|
||||
set_name_label ();
|
||||
set_tooltip (*_level_meter, _route->name ());
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::route_color_changed ()
|
||||
{
|
||||
_number_label.set_fixed_colors (gdk_color_to_rgba (color ()), gdk_color_to_rgba (color ()));
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::on_theme_changed ()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::on_size_request (Gtk::Requisition* r)
|
||||
{
|
||||
VBox::on_size_request (r);
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::on_size_allocate (Gtk::Allocation& a)
|
||||
{
|
||||
VBox::on_size_allocate (a);
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::parameter_changed (std::string const& p)
|
||||
{
|
||||
if (p == "editor-stereo-only-meters") {
|
||||
#if 0
|
||||
if (UIConfiguration::instance ().get_editor_stereo_only_meters ()) {
|
||||
_level_meter->set_max_audio_meter_count (2);
|
||||
} else {
|
||||
_level_meter->set_max_audio_meter_count (0);
|
||||
}
|
||||
#endif
|
||||
} else if (p == "time-axis-name-ellipsize-mode") {
|
||||
set_name_ellipsize_mode ();
|
||||
}
|
||||
}
|
||||
|
||||
string
|
||||
TrackRecordAxis::name () const
|
||||
{
|
||||
return _route->name ();
|
||||
}
|
||||
|
||||
Gdk::Color
|
||||
TrackRecordAxis::color () const
|
||||
{
|
||||
return RouteUI::route_color ();
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::set_name_label ()
|
||||
{
|
||||
string x = _route->name ();
|
||||
if (x != name_label.get_text ()) {
|
||||
name_label.set_text (x);
|
||||
}
|
||||
set_tooltip (name_label, _route->name ());
|
||||
|
||||
const int64_t track_number = _route->track_number ();
|
||||
assert (track_number > 0);
|
||||
_number_label.set_text (PBD::to_string (track_number));
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::route_active_changed ()
|
||||
{
|
||||
RouteUI::route_active_changed ();
|
||||
update_sensitivity ();
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::map_frozen ()
|
||||
{
|
||||
RouteUI::map_frozen ();
|
||||
|
||||
switch (track()->freeze_state()) {
|
||||
case Track::Frozen:
|
||||
_playlist_button.set_sensitive (false);
|
||||
break;
|
||||
default:
|
||||
_playlist_button.set_sensitive (true);
|
||||
break;
|
||||
}
|
||||
|
||||
update_sensitivity ();
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::update_sensitivity ()
|
||||
{
|
||||
bool en = _route->active ();
|
||||
monitor_input_button->set_sensitive (en);
|
||||
monitor_disk_button->set_sensitive (en);
|
||||
_input_button.set_sensitive (en);
|
||||
_ctrls.set_sensitive (en);
|
||||
|
||||
if (!is_track() || track()->mode() != ARDOUR::Normal) {
|
||||
_playlist_button.set_sensitive (false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::set_gui_extents (samplepos_t s, samplepos_t e)
|
||||
{
|
||||
_track_summary.set_gui_extents (s, e);
|
||||
}
|
||||
|
||||
bool
|
||||
TrackRecordAxis::rec_extent (samplepos_t& s, samplepos_t& e) const
|
||||
{
|
||||
return _track_summary.rec_extent (s, e);
|
||||
}
|
||||
|
||||
int
|
||||
TrackRecordAxis::summary_xpos () const
|
||||
{
|
||||
return _ctrls.get_width () - _track_summary.get_width ();
|
||||
}
|
||||
|
||||
int
|
||||
TrackRecordAxis::summary_width () const
|
||||
{
|
||||
return _track_summary.get_width ();
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::fast_update ()
|
||||
{
|
||||
if (_clear_meters) {
|
||||
_level_meter->clear_meters ();
|
||||
_clear_meters = false;
|
||||
}
|
||||
_level_meter->update_meters ();
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::reset_route_peak_display (Route* route)
|
||||
{
|
||||
if (_route && _route.get () == route) {
|
||||
reset_peak_display ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::reset_group_peak_display (RouteGroup* group)
|
||||
{
|
||||
if (_route && group == _route->route_group ()) {
|
||||
reset_peak_display ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::reset_peak_display ()
|
||||
{
|
||||
_route->shared_peak_meter ()->reset_max ();
|
||||
_clear_meters = true;
|
||||
}
|
||||
|
||||
bool
|
||||
TrackRecordAxis::playlist_click (GdkEventButton* ev)
|
||||
{
|
||||
if (ev->button != 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
build_playlist_menu ();
|
||||
_route->session ().selection().select_stripable_and_maybe_group (_route, false, true, 0);
|
||||
Gtkmm2ext::anchored_menu_popup (playlist_action_menu, &_playlist_button, "", 1, ev->time);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TrackRecordAxis::route_ops_click (GdkEventButton* ev)
|
||||
{
|
||||
if (ev->button != 3 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
build_route_ops_menu ();
|
||||
|
||||
_route->session ().selection().select_stripable_and_maybe_group (_route, false, true, 0);
|
||||
|
||||
Gtkmm2ext::anchored_menu_popup (_route_ops_menu, &_number_label, "", 1, ev->time);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::build_route_ops_menu ()
|
||||
{
|
||||
using namespace Menu_Helpers;
|
||||
|
||||
delete _route_ops_menu;
|
||||
_route_ops_menu = new Menu;
|
||||
_route_ops_menu->set_name ("ArdourContextMenu");
|
||||
|
||||
MenuList& items = _route_ops_menu->items ();
|
||||
|
||||
items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
|
||||
items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
|
||||
items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
|
||||
items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
|
||||
|
||||
items.push_back (SeparatorElem());
|
||||
|
||||
items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
|
||||
/* do not allow rename if the track is record-enabled */
|
||||
items.back().set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
|
||||
}
|
||||
|
||||
/* ****************************************************************************/
|
||||
|
||||
TrackRecordAxis::TrackSummary::TrackSummary (boost::shared_ptr<ARDOUR::Route> r)
|
||||
: _start (0)
|
||||
, _end (480000)
|
||||
, _xscale (1)
|
||||
, _last_playhead (0)
|
||||
, _rec_updating (false)
|
||||
, _rec_active (false)
|
||||
{
|
||||
_track = boost::dynamic_pointer_cast<Track> (r);
|
||||
assert (_track);
|
||||
|
||||
_track->PlaylistChanged.connect (_connections, invalidator (*this), boost::bind (&TrackSummary::playlist_changed, this), gui_context ());
|
||||
_track->playlist()->ContentsChanged.connect (_connections, invalidator (*this), boost::bind (&TrackSummary::playlist_changed, this), gui_context ());
|
||||
_track->presentation_info().PropertyChanged.connect (_connections, invalidator (*this), boost::bind (&TrackSummary::property_changed, this, _1), gui_context ());
|
||||
|
||||
_track->rec_enable_control()->Changed.connect (_connections, invalidator (*this), boost::bind (&TrackSummary::maybe_setup_rec_box, this), gui_context());
|
||||
_track->session().TransportStateChange.connect (_connections, invalidator (*this), boost::bind (&TrackSummary::maybe_setup_rec_box, this), gui_context());
|
||||
_track->session().TransportLooped.connect (_connections, invalidator (*this), boost::bind (&TrackSummary::maybe_setup_rec_box, this), gui_context());
|
||||
_track->session().RecordStateChanged.connect (_connections, invalidator (*this), boost::bind (&TrackSummary::maybe_setup_rec_box, this), gui_context());
|
||||
|
||||
}
|
||||
|
||||
TrackRecordAxis::TrackSummary::~TrackSummary ()
|
||||
{
|
||||
_rec_active = false;
|
||||
if (_rec_updating) {
|
||||
_screen_update_connection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::TrackSummary::render (Cairo::RefPtr<Cairo::Context> const& cr, cairo_rectangle_t* r)
|
||||
{
|
||||
cr->rectangle (r->x, r->y, r->width, r->height);
|
||||
cr->clip ();
|
||||
|
||||
RouteGroup* g = _track->route_group ();
|
||||
if (g && g->is_color()) {
|
||||
Gtkmm2ext::set_source_rgba (cr, GroupTabs::group_color (g));
|
||||
} else {
|
||||
Gtkmm2ext::set_source_rgba (cr, _track->presentation_info ().color ());
|
||||
}
|
||||
|
||||
double w = get_width();
|
||||
double h = get_height();
|
||||
|
||||
double ht = h - 2;
|
||||
double yc = 1 + ht / 2.;
|
||||
cr->set_line_width (ht);
|
||||
|
||||
_track->playlist()->foreach_region(sigc::bind (sigc::mem_fun (*this, &TrackSummary::render_region), cr, yc));
|
||||
|
||||
/* Record Boxes */
|
||||
if (_rec_rects.size () > 0) {
|
||||
Gtkmm2ext::set_source_rgba (cr, UIConfiguration::instance().color_mod("recording rect", "recording_rect"));
|
||||
for (std::vector<RecInfo>::const_iterator i = _rec_rects.begin (); i != _rec_rects.end (); ++i) {
|
||||
const samplepos_t rs = i->capture_start;
|
||||
const samplecnt_t re = i->capture_end;
|
||||
if (re > rs) {
|
||||
cr->move_to (sample_to_xpos (rs), yc);
|
||||
cr->line_to (sample_to_xpos (re), yc);
|
||||
cr->stroke ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* top & btm border */
|
||||
Gtkmm2ext::set_source_rgba (cr, UIConfiguration::instance().color ("neutral:backgroundest"));
|
||||
cr->set_line_width (1.0);
|
||||
cr->move_to (0, 0.5);
|
||||
cr->line_to (w, 0.5);
|
||||
cr->stroke ();
|
||||
cr->move_to (0, h);
|
||||
cr->line_to (w, h);
|
||||
cr->stroke ();
|
||||
|
||||
/* Playhead */
|
||||
Gtkmm2ext::set_source_rgba (cr, UIConfiguration::instance().color ("play head"));
|
||||
const double phx = sample_to_xpos (PublicEditor::instance().playhead_cursor ()->current_sample());
|
||||
cr->set_line_width (1.0);
|
||||
cr->move_to (floor (phx) + .5, 0);
|
||||
cr->line_to (floor (phx) + .5, h);
|
||||
cr->stroke ();
|
||||
_last_playhead = phx;
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::TrackSummary::render_region (boost::shared_ptr<ARDOUR::Region> r, Cairo::RefPtr<Cairo::Context> const& cr, double y)
|
||||
{
|
||||
const samplepos_t rp = r->position ();
|
||||
const samplecnt_t rl = r->length ();
|
||||
|
||||
if (rp > _start) {
|
||||
cr->move_to (sample_to_xpos (rp), y);
|
||||
} else {
|
||||
cr->move_to (0, y);
|
||||
}
|
||||
if (rp + rl > _start) {
|
||||
cr->line_to (sample_to_xpos (rp + rl), y);
|
||||
cr->stroke ();
|
||||
} else {
|
||||
cr->begin_new_path ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::TrackSummary::maybe_setup_rec_box ()
|
||||
{
|
||||
if (_track->session ().transport_stopped_or_stopping () || !(_track->session ().transport_rolling () || _track->session ().get_record_enabled ())) {
|
||||
/* stopped, or not roll/rec */
|
||||
if (_rec_updating) {
|
||||
_rec_rects.clear ();
|
||||
_screen_update_connection.disconnect();
|
||||
_rec_updating = false;
|
||||
_rec_active = false;
|
||||
set_dirty ();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_track->rec_enable_control()->get_value() || !_track->session ().actively_recording ()) {
|
||||
/* rolling but not (or no longer) recording [yet] */
|
||||
_rec_active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_rec_active) {
|
||||
const samplepos_t rs = _track->current_capture_start ();
|
||||
_rec_rects.push_back (RecInfo (rs, rs));
|
||||
}
|
||||
|
||||
_rec_active = true;
|
||||
|
||||
if (!_rec_updating) {
|
||||
_screen_update_connection.disconnect();
|
||||
_screen_update_connection = Timers::rapid_connect (sigc::mem_fun(*this, &TrackSummary::update_rec_box));
|
||||
_rec_updating = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::TrackSummary::update_rec_box ()
|
||||
{
|
||||
if (_rec_active && _rec_rects.size () > 0) {
|
||||
RecInfo& rect = _rec_rects.back ();
|
||||
rect.capture_start = _track->current_capture_start ();
|
||||
rect.capture_end = _track->current_capture_end ();
|
||||
set_dirty ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::TrackSummary::playhead_position_changed (samplepos_t p)
|
||||
{
|
||||
int const o = _last_playhead;
|
||||
int const n = sample_to_xpos (p);
|
||||
if (o != n) {
|
||||
int a = max (2, min (o, n));
|
||||
int b = max (o, n);
|
||||
|
||||
cairo_rectangle_t r;
|
||||
r.x = a - 2;
|
||||
r.y = 0;
|
||||
r.width = b - a + 4;
|
||||
r.height = get_height ();
|
||||
set_dirty (&r);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::TrackSummary::playlist_changed ()
|
||||
{
|
||||
set_dirty ();
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::TrackSummary::property_changed (PropertyChange const& what_changed)
|
||||
{
|
||||
if (what_changed.contains (Properties::color)) {
|
||||
set_dirty ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::TrackSummary::on_size_request (Gtk::Requisition* req)
|
||||
{
|
||||
req->width = 200;
|
||||
req->height = 16;
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::TrackSummary::on_size_allocate (Gtk::Allocation& a)
|
||||
{
|
||||
CairoWidget::on_size_allocate (a);
|
||||
|
||||
if (_end > _start) {
|
||||
_xscale = static_cast<double> (a.get_width ()) / (_end - _start);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TrackRecordAxis::TrackSummary::set_gui_extents (samplepos_t start, samplepos_t end)
|
||||
{
|
||||
if (_start == start && _end == end) {
|
||||
return;
|
||||
}
|
||||
_start = start;
|
||||
_end = end;
|
||||
_xscale = static_cast<double> (get_width ()) / (_end - _start);
|
||||
|
||||
set_dirty ();
|
||||
}
|
||||
|
||||
bool
|
||||
TrackRecordAxis::TrackSummary::on_button_press_event (GdkEventButton* ev)
|
||||
{
|
||||
if (_track->session ().actively_recording ()) {
|
||||
return false;
|
||||
}
|
||||
// use _start + ev->x / _xscale
|
||||
_track->session ().request_locate (_start + (double) (_end - _start) * ev->x / get_width ());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TrackRecordAxis::TrackSummary::rec_extent (samplepos_t& start, samplepos_t& end) const
|
||||
{
|
||||
if (_rec_rects.size () == 0) {
|
||||
return false;
|
||||
}
|
||||
for (std::vector<RecInfo>::const_iterator i = _rec_rects.begin (); i != _rec_rects.end (); ++i) {
|
||||
start = std::min (start, i->capture_start);
|
||||
end = std::max (end, i->capture_end);
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* Copyright (C) 2020 Robin Gareus <robin@gareus.org>
|
||||
*
|
||||
* 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.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef __gtkardour_track_record_axis_h_
|
||||
#define __gtkardour_track_record_axis_h_
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
#include <gtkmm/alignment.h>
|
||||
#include <gtkmm/box.h>
|
||||
#include <gtkmm/drawingarea.h>
|
||||
#include <gtkmm/eventbox.h>
|
||||
#include <gtkmm/separator.h>
|
||||
#include <gtkmm/sizegroup.h>
|
||||
|
||||
#include "pbd/stateful.h"
|
||||
|
||||
#include "ardour/ardour.h"
|
||||
#include "ardour/types.h"
|
||||
|
||||
#include "widgets/ardour_button.h"
|
||||
#include "widgets/ardour_spacer.h"
|
||||
|
||||
#include "io_button.h"
|
||||
#include "level_meter.h"
|
||||
#include "route_ui.h"
|
||||
|
||||
namespace ARDOUR
|
||||
{
|
||||
class Region;
|
||||
class Route;
|
||||
class RouteGroup;
|
||||
class Session;
|
||||
class Track;
|
||||
}
|
||||
|
||||
class LevelMeterVBox;
|
||||
class RouteGroupMenu;
|
||||
|
||||
class TrackRecordAxis : public Gtk::VBox, public AxisView, public RouteUI
|
||||
{
|
||||
public:
|
||||
TrackRecordAxis (ARDOUR::Session*, boost::shared_ptr<ARDOUR::Route>);
|
||||
~TrackRecordAxis ();
|
||||
|
||||
/* AxisView */
|
||||
std::string name () const;
|
||||
Gdk::Color color () const;
|
||||
|
||||
boost::shared_ptr<ARDOUR::Stripable> stripable() const {
|
||||
return RouteUI::stripable();
|
||||
}
|
||||
|
||||
void set_session (ARDOUR::Session* s);
|
||||
|
||||
void fast_update ();
|
||||
void set_gui_extents (samplepos_t, samplepos_t);
|
||||
bool rec_extent (samplepos_t&, samplepos_t&) const;
|
||||
int summary_xpos () const;
|
||||
int summary_width () const;
|
||||
|
||||
static PBD::Signal1<void, TrackRecordAxis*> CatchDeletion;
|
||||
|
||||
protected:
|
||||
void self_delete ();
|
||||
|
||||
void on_size_allocate (Gtk::Allocation&);
|
||||
void on_size_request (Gtk::Requisition*);
|
||||
|
||||
/* AxisView */
|
||||
std::string state_id () const;
|
||||
|
||||
/* route UI */
|
||||
void set_button_names ();
|
||||
void blink_rec_display (bool onoff);
|
||||
void route_active_changed ();
|
||||
void map_frozen ();
|
||||
|
||||
private:
|
||||
void on_theme_changed ();
|
||||
void parameter_changed (std::string const& p);
|
||||
|
||||
void set_name_label ();
|
||||
|
||||
void reset_peak_display ();
|
||||
void reset_route_peak_display (ARDOUR::Route*);
|
||||
void reset_group_peak_display (ARDOUR::RouteGroup*);
|
||||
|
||||
bool playlist_click (GdkEventButton*);
|
||||
bool route_ops_click (GdkEventButton*);
|
||||
void build_route_ops_menu ();
|
||||
|
||||
/* RouteUI */
|
||||
void route_property_changed (const PBD::PropertyChange&);
|
||||
void route_color_changed ();
|
||||
void update_sensitivity ();
|
||||
|
||||
bool _clear_meters;
|
||||
|
||||
Gtk::Table _ctrls;
|
||||
Gtk::Menu* _route_ops_menu;
|
||||
|
||||
LevelMeterVBox* _level_meter;
|
||||
IOButton _input_button;
|
||||
ArdourWidgets::ArdourButton _number_label;
|
||||
ArdourWidgets::ArdourButton _playlist_button;
|
||||
ArdourWidgets::ArdourVSpacer _vseparator;
|
||||
|
||||
Glib::RefPtr<Gtk::SizeGroup> _ctrls_button_size_group;
|
||||
Glib::RefPtr<Gtk::SizeGroup> _monitor_ctrl_size_group;
|
||||
|
||||
static bool _size_group_initialized;
|
||||
static Glib::RefPtr<Gtk::SizeGroup> _track_number_size_group;
|
||||
|
||||
PBD::ScopedConnectionList _route_connections;
|
||||
|
||||
struct RecInfo {
|
||||
RecInfo (samplepos_t s, samplepos_t e)
|
||||
: capture_start (s)
|
||||
, capture_end (e)
|
||||
{}
|
||||
samplepos_t capture_start;
|
||||
samplepos_t capture_end;
|
||||
};
|
||||
|
||||
class TrackSummary : public CairoWidget
|
||||
{
|
||||
public:
|
||||
TrackSummary (boost::shared_ptr<ARDOUR::Route>);
|
||||
~TrackSummary ();
|
||||
|
||||
void playhead_position_changed (samplepos_t p);
|
||||
void set_gui_extents (samplepos_t, samplepos_t);
|
||||
bool rec_extent (samplepos_t&, samplepos_t&) const;
|
||||
|
||||
protected:
|
||||
void render (Cairo::RefPtr<Cairo::Context> const&, cairo_rectangle_t*);
|
||||
void on_size_request (Gtk::Requisition*);
|
||||
void on_size_allocate (Gtk::Allocation&);
|
||||
bool on_button_press_event (GdkEventButton*);
|
||||
|
||||
private:
|
||||
void render_region (boost::shared_ptr<ARDOUR::Region>, Cairo::RefPtr<Cairo::Context> const&, double);
|
||||
void playlist_changed ();
|
||||
void property_changed (PBD::PropertyChange const&);
|
||||
void maybe_setup_rec_box ();
|
||||
void update_rec_box ();
|
||||
|
||||
double sample_to_xpos (samplepos_t p) const
|
||||
{
|
||||
return (p - _start) * _xscale;
|
||||
}
|
||||
|
||||
boost::shared_ptr<ARDOUR::Track> _track;
|
||||
samplepos_t _start;
|
||||
samplepos_t _end;
|
||||
double _xscale;
|
||||
double _last_playhead;
|
||||
bool _rec_updating;
|
||||
bool _rec_active;
|
||||
|
||||
std::vector<RecInfo> _rec_rects;
|
||||
PBD::ScopedConnectionList _connections;
|
||||
sigc::connection _screen_update_connection;
|
||||
};
|
||||
|
||||
TrackSummary _track_summary;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -84,6 +84,7 @@ UI_CONFIG_VARIABLE (float, meter_hold, "meter-hold", 100.0f)
|
|||
UI_CONFIG_VARIABLE (ARDOUR::VUMeterStandard, meter_vu_standard, "meter-vu-standard", ARDOUR::MeteringVUstandard)
|
||||
UI_CONFIG_VARIABLE (ARDOUR::MeterLineUp, meter_line_up_level, "meter-line-up-level", ARDOUR::MeteringLineUp18)
|
||||
UI_CONFIG_VARIABLE (ARDOUR::MeterLineUp, meter_line_up_din, "meter-line-up-din", ARDOUR::MeteringLineUp15)
|
||||
UI_CONFIG_VARIABLE (ARDOUR::InputMeterLayout, input_meter_layout, "input-meter-layout", ARDOUR::LayoutAutomatic)
|
||||
UI_CONFIG_VARIABLE (float, meter_peak, "meter-peak", 0.0f)
|
||||
UI_CONFIG_VARIABLE (bool, meter_style_led, "meter-style-led", false)
|
||||
UI_CONFIG_VARIABLE (bool, show_editor_meter, "show-editor-meter", true)
|
||||
|
|
|
@ -127,6 +127,7 @@ gtk2_ardour_sources = [
|
|||
'group_tabs.cc',
|
||||
'gui_object.cc',
|
||||
'idleometer.cc',
|
||||
'input_port_monitor.cc',
|
||||
'insert_remove_time_dialog.cc',
|
||||
'instrument_selector.cc',
|
||||
'interthread_progress_window.cc',
|
||||
|
@ -225,6 +226,7 @@ gtk2_ardour_sources = [
|
|||
'public_editor.cc',
|
||||
'quantize_dialog.cc',
|
||||
'rc_option_editor.cc',
|
||||
'recorder_group_tabs.cc',
|
||||
'recorder_ui.cc',
|
||||
'region_editor.cc',
|
||||
'region_gain_line.cc',
|
||||
|
@ -279,6 +281,7 @@ gtk2_ardour_sources = [
|
|||
'time_info_box.cc',
|
||||
'time_selection.cc',
|
||||
'timers.cc',
|
||||
'track_record_axis.cc',
|
||||
'track_selection.cc',
|
||||
'track_view_list.cc',
|
||||
'transform_dialog.cc',
|
||||
|
|
Loading…
Reference in New Issue