Default_Play_Speed: simple varispeed GUI

This commit is contained in:
Ben Loftis 2021-06-18 15:26:22 -05:00
parent af19e1e7e7
commit 5b8835483e
6 changed files with 252 additions and 152 deletions

View File

@ -82,6 +82,7 @@
#include "video_timeline.h"
#include "widgets/ardour_button.h"
#include "widgets/ardour_dropdown.h"
#include "widgets/ardour_spacer.h"
#include "add_route_dialog.h"
@ -484,6 +485,9 @@ private:
void update_transport_clocks (samplepos_t pos);
void record_state_changed ();
ArdourWidgets::ArdourDropdown _varispeed_pulldown;
void set_default_play_spd_from_menu (double spd);
std::list<MidiTracer*> _midi_tracer_windows;
/* Transport Control */

View File

@ -37,6 +37,9 @@
#include <iostream>
#include <cmath>
#include <gtkmm/menu.h>
#include <gtkmm/menuitem.h>
#include <sigc++/bind.h>
#include <gtkmm/settings.h>
@ -79,6 +82,7 @@ using namespace ArdourWidgets;
using namespace Gtk;
using namespace Glib;
using namespace ARDOUR_UI_UTILS;
using namespace Menu_Helpers;
void
ARDOUR_UI::setup_tooltips ()
@ -98,6 +102,8 @@ ARDOUR_UI::setup_tooltips ()
set_tip (latency_disable_button, _("Disable all Plugin Delay Compensation. This results in the shortest delay from live input to output, but any paths with delay-causing plugins will sound later than those without."));
set_tip (_varispeed_pulldown, _("Varispeed: change the default playback and recording speed"));
synchronize_sync_source_and_video_pullup ();
editor->setup_tooltips ();
@ -528,9 +534,9 @@ ARDOUR_UI::setup_transport ()
ssbox->set_spacing (PX_SCALE(2));
ssbox->pack_start (sync_button, false, false, 0);
ssbox->pack_start (shuttle_box, true, true, 0);
ssbox->pack_start (_varispeed_pulldown, false, false, 0);
ssbox->pack_start (*shuttle_box.info_button(), false, false, 0);
/* and the main table layout */
int vpadding = 1;
int hpadding = 2;
@ -630,6 +636,20 @@ ARDOUR_UI::setup_transport ()
transport_table.attach (editor_visibility_button, TCOL, 0, 1 , FILL, SHRINK, hpadding, vpadding);
++col;
// _varispeed_pulldown.set_icon (ArdourIcon::RecButton);
_varispeed_pulldown.set_text(_("Vari"));
// _varispeed_pulldown.set_icon(record_tape_red);
_varispeed_pulldown.AddMenuElem (MenuElem (_("None"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_default_play_spd_from_menu), 1.0)));
_varispeed_pulldown.AddMenuElem (SeparatorElem());
_varispeed_pulldown.AddMenuElem (MenuElem (_("-10 cents"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_default_play_spd_from_menu), ShuttleControl::cents_as_speed(-10, false))));
_varispeed_pulldown.AddMenuElem (MenuElem (_("+10 cents"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_default_play_spd_from_menu), ShuttleControl::cents_as_speed(10, false))));
_varispeed_pulldown.AddMenuElem (SeparatorElem());
_varispeed_pulldown.AddMenuElem (MenuElem (_("-1 semitone"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_default_play_spd_from_menu), ShuttleControl::semitones_as_speed(-1, false))));
_varispeed_pulldown.AddMenuElem (MenuElem (_("+1 semitone"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_default_play_spd_from_menu), ShuttleControl::semitones_as_speed(1, false))));
_varispeed_pulldown.AddMenuElem (SeparatorElem());
_varispeed_pulldown.AddMenuElem (MenuElem (_("-1 octave"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_default_play_spd_from_menu), ShuttleControl::semitones_as_speed(-12, false))));
_varispeed_pulldown.AddMenuElem (MenuElem (_("+1 octave"), sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::set_default_play_spd_from_menu), ShuttleControl::semitones_as_speed(12, false))));
/* initialize */
latency_switch_changed ();
session_latency_updated (true);
@ -749,6 +769,12 @@ ARDOUR_UI::layered_button_clicked ()
}
}
void
ARDOUR_UI::set_default_play_spd_from_menu (double spd)
{
_session->set_default_play_speed(spd);
}
void
ARDOUR_UI::solo_blink (bool onoff)
{

View File

@ -23,6 +23,9 @@
#include <algorithm>
#include <gtkmm/stock.h>
#include <gtkmm/menu.h>
#include <gtkmm/menuitem.h>
#include "pbd/string_convert.h"
#include "ardour/audioengine.h"
@ -66,6 +69,8 @@ using namespace Gtkmm2ext;
using namespace ArdourWidgets;
using namespace Gtk;
using namespace std;
using namespace Menu_Helpers;
#define PX_SCALE(px) std::max ((float)px, rintf ((float)px* UIConfiguration::instance ().get_ui_scale ()))
@ -231,6 +236,9 @@ RecorderUI::RecorderUI ()
_button_table.attach (_btn_new_plist_rec, col, col + 2, 1, 2 , FILL, SHRINK, hpadding, vpadding);
col += 2;
_button_table.attach (*(manage (new ArdourVSpacer ())), col, col + 1, 0, 2, FILL, FILL, spacepad, vpadding);
col += 1;
_toolbar.pack_start (_button_table, false, false);
_toolbar.pack_end (_btn_peak_reset, false, false, 4);
_toolbar.pack_end (_remain_info_box, false, false, 4);

View File

@ -46,6 +46,7 @@
#include "input_port_monitor.h"
#include "rec_info_box.h"
#include "shuttle_control.h"
#include "transport_control_ui.h"
namespace ARDOUR {

View File

@ -44,6 +44,7 @@
#include "actions.h"
#include "rgb_macros.h"
#include "shuttle_control.h"
#include "timers.h"
#include "pbd/i18n.h"
@ -59,17 +60,90 @@ gboolean qt (gboolean, gint, gint, gboolean, Gtk::Tooltip*, gpointer)
return FALSE;
}
ShuttleInfoButton::~ShuttleInfoButton ()
{
delete disp_context_menu;
}
ShuttleInfoButton::ShuttleInfoButton ()
{
set_layout_font (UIConfiguration::instance().get_NormalFont());
set_sizing_text (S_("LogestShuttle|+00 st"));
set_tooltip (*this, _("Speed Display (Context-click for options)"));
set_name ("shuttle text");
set_visual_state (Gtkmm2ext::NoVisualState);
set_elements (ArdourButton::Text);
Config->ParameterChanged.connect (parameter_connection, MISSING_INVALIDATOR, boost::bind (&ShuttleInfoButton::parameter_changed, this, _1), gui_context());
add_events (Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK);
disp_context_menu = 0;
_ignore_change = false;
}
void
ShuttleInfoButton::set_shuttle_units (ShuttleUnits s)
{
if (_ignore_change) {
return;
}
Config->set_shuttle_units (s);
}
void
ShuttleInfoButton::parameter_changed (std::string p)
{
/* units changed; recreate the menu when it is next opened to show the current selection*/
if (p == "shuttle-units") {
delete disp_context_menu;
disp_context_menu = 0;
}
}
bool
ShuttleInfoButton::on_button_press_event (GdkEventButton* ev)
{
if (Keyboard::is_context_menu_event (ev)) {
if (disp_context_menu == 0) {
build_disp_context_menu ();
}
disp_context_menu->popup (ev->button, ev->time);
return true;
}
return true;
}
void
ShuttleInfoButton::build_disp_context_menu ()
{
PBD::Unwinder<bool> uw (_ignore_change, true);
using namespace Menu_Helpers;
disp_context_menu = new Gtk::Menu();
MenuList& items = disp_context_menu->items();
RadioMenuItem::Group units_group;
items.push_back (RadioMenuElem (units_group, _("Percent"), sigc::bind (sigc::mem_fun (*this, &ShuttleInfoButton::set_shuttle_units), Percentage)));
if (Config->get_shuttle_units() == Percentage) {
static_cast<RadioMenuItem*>(&items.back())->set_active();
}
items.push_back (RadioMenuElem (units_group, _("Semitones"), sigc::bind (sigc::mem_fun (*this, &ShuttleInfoButton::set_shuttle_units), Semitones)));
if (Config->get_shuttle_units() == Semitones) {
static_cast<RadioMenuItem*>(&items.back())->set_active();
}
}
ShuttleControl::ShuttleControl ()
: _controllable (new ShuttleControllable (*this))
, binding_proxy (_controllable)
{
_info_button.set_layout_font (UIConfiguration::instance().get_NormalFont());
_info_button.set_sizing_text (S_("LogestShuttle|< +00 st"));
_info_button.set_name ("shuttle text");
_info_button.set_sensitive (false);
_info_button.set_visual_state (Gtkmm2ext::NoVisualState);
_info_button.set_elements (ArdourButton::Text);
set_tooltip (*this, _("Shuttle speed control (Context-click for options)"));
pattern = 0;
@ -101,6 +175,7 @@ ShuttleControl::ShuttleControl ()
Config->ParameterChanged.connect (parameter_connection, MISSING_INVALIDATOR, boost::bind (&ShuttleControl::parameter_changed, this, _1), gui_context());
UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &ShuttleControl::set_colors));
Timers::blink_connect (sigc::mem_fun (*this, &ShuttleControl::do_blink));
/* gtkmm 2.4: the C++ wrapper doesn't work */
g_signal_connect ((GObject*) gobj(), "query-tooltip", G_CALLBACK (qt), NULL);
@ -114,6 +189,17 @@ ShuttleControl::~ShuttleControl ()
delete shuttle_context_menu;
}
void
ShuttleControl::do_blink (bool onoff)
{
if (!shuttle_grabbed && _session && _session->default_play_speed()!=1.0) {
_info_button.set_active(onoff);
} else {
_info_button.set_active(false);
}
}
void
ShuttleControl::set_session (Session *s)
{
@ -122,6 +208,7 @@ ShuttleControl::set_session (Session *s)
if (_session) {
set_sensitive (true);
_session->add_controllable (_controllable);
_info_button.set_session (s);
} else {
set_sensitive (false);
}
@ -158,14 +245,62 @@ ShuttleControl::on_size_allocate (Gtk::Allocation& alloc)
void
ShuttleControl::map_transport_state ()
{
float speed;
float speed = 0.0;
float actual_speed = 0.0;
char buf[32];
if (!_session) {
speed = 0.0;
} else {
if (_session) {
speed = _session->actual_speed ();
actual_speed = speed;
if (shuttle_grabbed) {
speed = requested_speed;
} else if (_session->default_play_speed()!=1.0) {
/*use has selected a varispeed AND the shuttle widget is not held ... show the varispeed*/
actual_speed = _session->default_play_speed();
}
}
if (actual_speed != 0) {
if (_session->default_play_speed()==1.0) {
if (actual_speed == _session->default_play_speed()) {
snprintf (buf, sizeof (buf), "%s", _("Play"));
} else if (Config->get_shuttle_units() == Percentage) {
{
if (actual_speed < 0.0) {
snprintf (buf, sizeof (buf), "< %.1f%%", -actual_speed * 100.f);
} else {
snprintf (buf, sizeof (buf), "> %.1f%%", actual_speed * 100.f);
}
}
} else {
bool reversed;
int semi = speed_as_semitones (actual_speed, reversed);
if (reversed) {
snprintf (buf, sizeof (buf), _("< %+2d st"), semi);
} else {
snprintf (buf, sizeof (buf), _("> %+2d st"), semi);
}
}
} else { /*default_play_speed (varispeed) is always forward */
if (Config->get_shuttle_units() == Percentage) {
snprintf (buf, sizeof (buf), "%.1f%%", actual_speed * 100.f);
} else {
bool reversed;
int semi = speed_as_semitones (actual_speed, reversed);
if (abs(semi)<1) {
int cents = speed_as_cents (actual_speed, reversed);
snprintf (buf, sizeof (buf), _("%+2d c"), cents);
} else {
snprintf (buf, sizeof (buf), _("%+2d st"), semi);
}
}
}
} else {
snprintf (buf, sizeof (buf), "%s", _("Stop"));
}
_info_button.set_text(buf);
if ( (fabsf( speed - last_speed_displayed) < 0.005f) // dead-zone
&& !( speed == 1.f && last_speed_displayed != 1.f)
&& !( speed == 0.f && last_speed_displayed != 0.f)
@ -201,21 +336,7 @@ ShuttleControl::build_shuttle_context_menu ()
shuttle_context_menu = new Menu();
MenuList& items = shuttle_context_menu->items();
Menu* units_menu = manage (new Menu);
MenuList& units_items = units_menu->items();
RadioMenuItem::Group units_group;
units_items.push_back (RadioMenuElem (units_group, _("Percent"), sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_units), Percentage)));
if (Config->get_shuttle_units() == Percentage) {
static_cast<RadioMenuItem*>(&units_items.back())->set_active();
}
units_items.push_back (RadioMenuElem (units_group, _("Semitones"), sigc::bind (sigc::mem_fun (*this, &ShuttleControl::set_shuttle_units), Semitones)));
if (Config->get_shuttle_units() == Semitones) {
static_cast<RadioMenuItem*>(&units_items.back())->set_active();
}
items.push_back (MenuElem (_("Units"), *units_menu));
if (Config->get_shuttle_units() == Percentage) {
{
RadioMenuItem::Group speed_group;
/* XXX this code assumes that Config->get_max_transport_speed() returns 8 */
@ -249,19 +370,6 @@ ShuttleControl::build_shuttle_context_menu ()
items.push_back (MenuElem (_("Maximum speed"), *speed_menu));
}
items.push_back (SeparatorElem ());
items.push_back (MenuElem (_("Reset to 100%"), sigc::mem_fun (*this, &ShuttleControl::reset_speed)));
}
void
ShuttleControl::reset_speed ()
{
if (!_session) {
return;
}
_session->reset_transport_speed ();
}
void
@ -356,63 +464,6 @@ ShuttleControl::on_query_tooltip (int, int, bool, const Glib::RefPtr<Gtk::Toolti
return false;
}
bool
ShuttleControl::on_scroll_event (GdkEventScroll* ev)
{
if (!_session) {
return true;
}
bool semis = (Config->get_shuttle_units() == Semitones);
switch (ev->direction) {
case GDK_SCROLL_UP:
case GDK_SCROLL_RIGHT:
if (semis) {
if (shuttle_fract == 0) {
shuttle_fract = semitones_as_fract (-24, false);
} else {
bool rev;
int st = fract_as_semitones (shuttle_fract, rev);
st += (rev ? -1 : 1);
if (st < -24) {
st = -24;
rev = !rev;
}
shuttle_fract = semitones_as_fract (st, rev);
}
} else {
shuttle_fract += 0.00125;
}
break;
case GDK_SCROLL_DOWN:
case GDK_SCROLL_LEFT:
if (semis) {
if (shuttle_fract == 0) {
shuttle_fract = semitones_as_fract (-24, true);
} else {
bool rev;
int st = fract_as_semitones (shuttle_fract, rev);
st += (rev ? 1 : -1);
if (st < -24) {
st = -24;
rev = !rev;
}
shuttle_fract = semitones_as_fract (st, rev);
}
} else {
shuttle_fract -= 0.00125;
}
break;
default:
return false;
}
use_shuttle_fract (true);
return true;
}
bool
ShuttleControl::on_motion_notify_event (GdkEventMotion* ev)
{
@ -466,6 +517,30 @@ ShuttleControl::speed_as_semitones (float speed, bool& reverse)
}
}
int
ShuttleControl::speed_as_cents (float speed, bool& reverse)
{
assert (speed != 0.0);
if (speed < 0.0) {
reverse = true;
return (int) ceilf (1200.0 * fast_log2 (-speed));
} else {
reverse = false;
return (int) ceilf (1200.0 * fast_log2 (speed));
}
}
float
ShuttleControl::cents_as_speed (int cents, bool reverse)
{
if (reverse) {
return -pow (2.0, (cents / 1200.0));
} else {
return pow (2.0, (cents / 1200.0));
}
}
float
ShuttleControl::semitones_as_speed (int semi, bool reverse)
{
@ -519,6 +594,7 @@ ShuttleControl::use_shuttle_fract (bool force, bool zero_ok)
speed = 0.0;
}
} else {
shuttle_fract = shuttle_fract*shuttle_fract*shuttle_fract; // ^3 preserves the sign;
speed = shuttle_max_speed * shuttle_fract;
}
@ -571,12 +647,8 @@ ShuttleControl::render (Cairo::RefPtr<Cairo::Context> const& ctx, cairo_rectangl
cairo_stroke (cr);
float speed = 0.0;
float actual_speed = 0.0;
char buf[32];
if (_session) {
speed = _session->actual_speed ();
actual_speed = speed;
if (shuttle_grabbed) {
speed = requested_speed;
}
@ -604,34 +676,8 @@ ShuttleControl::render (Cairo::RefPtr<Cairo::Context> const& ctx, cairo_rectangl
}
cairo_fill(cr);
/* text */
if (actual_speed != 0) {
if (Config->get_shuttle_units() == Percentage) {
if (actual_speed == 1.0) {
snprintf (buf, sizeof (buf), "%s", _("Play"));
} else {
if (actual_speed < 0.0) {
snprintf (buf, sizeof (buf), "< %.1f%%", -actual_speed * 100.f);
} else {
snprintf (buf, sizeof (buf), "> %.1f%%", actual_speed * 100.f);
}
}
} else {
bool reversed;
int semi = speed_as_semitones (actual_speed, reversed);
if (reversed) {
snprintf (buf, sizeof (buf), _("< %+2d st"), semi);
} else {
snprintf (buf, sizeof (buf), _("> %+2d st"), semi);
}
}
} else {
snprintf (buf, sizeof (buf), "%s", _("Stop"));
}
last_speed_displayed = actual_speed;
_info_button.set_text (buf);
last_speed_displayed = speed;
#if 0
if (UIConfiguration::instance().get_widget_prelight()) {
@ -644,15 +690,6 @@ ShuttleControl::render (Cairo::RefPtr<Cairo::Context> const& ctx, cairo_rectangl
#endif
}
void
ShuttleControl::set_shuttle_units (ShuttleUnits s)
{
if (_ignore_change) {
return;
}
Config->set_shuttle_units (s);
}
ShuttleControl::ShuttleControllable::ShuttleControllable (ShuttleControl& s)
: PBD::Controllable (X_("Shuttle"))
, sc (s)
@ -674,17 +711,15 @@ ShuttleControl::ShuttleControllable::get_value () const
void
ShuttleControl::parameter_changed (std::string p)
{
if (p == "shuttle-max-speed") {
if (p == "shuttle-units") {
map_transport_state ();
} else if (p == "shuttle-max-speed") {
shuttle_max_speed = Config->get_shuttle_max_speed ();
last_speed_displayed = -99999999;
map_transport_state ();
use_shuttle_fract (true);
} else if (p == "shuttle-units") {
last_speed_displayed = -99999999;
map_transport_state ();
use_shuttle_fract (true);
delete shuttle_context_menu;
shuttle_context_menu = 0;
shuttle_context_menu=0;
}
}

View File

@ -36,6 +36,25 @@ namespace Gtk {
class Menu;
}
class ShuttleInfoButton : public ArdourWidgets::ArdourButton, public ARDOUR::SessionHandlePtr
{
public:
ShuttleInfoButton ();
~ShuttleInfoButton ();
bool on_button_press_event (GdkEventButton*);
void set_shuttle_units (ARDOUR::ShuttleUnits s);
private:
void parameter_changed (std::string);
void build_disp_context_menu ();
Gtk::Menu* disp_context_menu;
PBD::ScopedConnection parameter_connection;
bool _ignore_change;
};
class ShuttleControl : public CairoWidget, public ARDOUR::SessionHandlePtr
{
public:
@ -47,6 +66,8 @@ public:
double get_shuttle_fract () const { return shuttle_fract; }
void set_session (ARDOUR::Session*);
void do_blink (bool);
struct ShuttleControllable : public PBD::Controllable {
ShuttleControllable (ShuttleControl&);
void set_value (double, PBD::Controllable::GroupControlDisposition group_override);
@ -63,6 +84,17 @@ public:
ArdourWidgets::ArdourButton* info_button () { return &_info_button; }
public:
static int speed_as_semitones (float, bool&);
static int fract_as_semitones (float, bool&);
static float semitones_as_speed (int, bool);
static float semitones_as_fract (int, bool);
static int speed_as_cents (float, bool&);
static float cents_as_speed (int, bool);
protected:
bool _hovering;
float shuttle_max_speed;
@ -76,21 +108,21 @@ protected:
cairo_pattern_t* shine_pattern;
ARDOUR::microseconds_t last_shuttle_request;
PBD::ScopedConnection parameter_connection;
ArdourWidgets::ArdourButton _info_button;
ShuttleInfoButton _info_button;
Gtk::Menu* shuttle_context_menu;
ArdourWidgets::BindingProxy binding_proxy;
float bg_r, bg_g, bg_b;
void build_shuttle_context_menu ();
void set_shuttle_max_speed (float);
void reset_speed ();
bool on_enter_notify_event (GdkEventCrossing*);
bool on_leave_notify_event (GdkEventCrossing*);
bool on_button_press_event (GdkEventButton*);
bool on_button_release_event(GdkEventButton*);
bool on_scroll_event (GdkEventScroll*);
bool on_motion_notify_event(GdkEventMotion*);
bool on_button_press_event_for_display (GdkEventButton*);
void render (Cairo::RefPtr<Cairo::Context> const&, cairo_rectangle_t*);
void on_size_allocate (Gtk::Allocation&);
@ -102,12 +134,6 @@ protected:
void set_shuttle_units (ARDOUR::ShuttleUnits);
int speed_as_semitones (float, bool&);
int fract_as_semitones (float, bool&);
float semitones_as_speed (int, bool);
float semitones_as_fract (int, bool);
bool _ignore_change;
};