a) completely refactor abstract UI code

b) single-thread Tranzport implementation
c) implement BasicUI to share functionality across multiple
     controllers
d) various minor fixes here and there


git-svn-id: svn://localhost/trunk/ardour2@468 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2006-04-24 22:45:19 +00:00
parent 484debb45c
commit 028e1ebc4a
38 changed files with 1218 additions and 1084 deletions

View file

@ -35,7 +35,8 @@ opts.AddOptions(
BoolOption('VERSIONED', 'Add version information to ardour/gtk executable name inside the build directory', 0),
EnumOption('DIST_TARGET', 'Build target for cross compiling packagers', 'auto', allowed_values=('auto', 'i386', 'i686', 'x86_64', 'powerpc', 'tiger', 'panther', 'none' ), ignorecase=2),
BoolOption('FPU_OPTIMIZATION', 'Build runtime checked assembler code', 1),
BoolOption('FFT_ANALYSIS', 'Include FFT analysis window', 0)
BoolOption('FFT_ANALYSIS', 'Include FFT analysis window', 0),
BoolOption('SURFACES', 'Build support for control surfaces', 0)
)
#----------------------------------------------------------------------
@ -389,8 +390,6 @@ libraries['libgnomecanvas2'].ParseConfig ('pkg-config --cflags --libs libgnomeca
libraries['glade2'] = LibraryInfo()
libraries['glade2'].ParseConfig ('pkg-config --cflags --libs libglade-2.0')
libraries['usb'] = LibraryInfo (LIBS='usb')
#libraries['flowcanvas'] = LibraryInfo(LIBS='flowcanvas', LIBPATH='#/libs/flowcanvas', CPPPATH='#libs/flowcanvas')
libraries['ardour'] = LibraryInfo (LIBS='ardour', LIBPATH='#libs/ardour', CPPPATH='#libs/ardour')
@ -403,6 +402,21 @@ libraries['fst'] = LibraryInfo()
if env['VST']:
libraries['fst'].ParseConfig('pkg-config --cflags --libs libfst')
#
# Check for libusb
libraries['usb'] = LibraryInfo ()
conf = Configure (libraries['usb'])
if conf.CheckLib ('usb', 'usb_interrupt_write'):
have_libusb = 1
else:
have_libusb = 0
libraries['usb'] = conf.Finish ()
#
#
# Audio/MIDI library (needed for MIDI, since audio is all handled via JACK)
#
@ -518,10 +532,13 @@ else:
'gtk2_ardour'
]
surface_subdirs = [
'libs/surfaces/tranzport',
'libs/surfaces/generic_midi'
]
surface_subdirs = []
if env['SURFACES']:
surface_subdirs += [ 'libs/surfaces/generic_midi' ]
if have_libusb:
surface_subdirs += [ 'libs/surfaces/tranzport' ]
opts.Save('scache.conf', env)
Help(opts.GenerateHelpText(env))

View file

@ -213,7 +213,6 @@ if env['FFT_ANALYSIS']:
intl_files += extra_sources
gtkardour.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
gtkardour.Append(CXXFLAGS="-DDATA_DIR=\\\""+final_prefix+"/share\\\"")
gtkardour.Append(CXXFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"")
gtkardour.Append(CXXFLAGS="-DGLADEPATH=\\\""+final_prefix+"/share/ardour/glade\\\"")

View file

@ -446,6 +446,14 @@ ARDOUR_UI::install_actions ()
void
ARDOUR_UI::toggle_control_protocol (ControlProtocolInfo* cpi)
{
if (!session) {
/* this happens when we build the menu bar when control protocol support
has been used in the past for some given protocol - the item needs
to be made active, but there is no session yet.
*/
return;
}
if (cpi->protocol == 0) {
ControlProtocolManager::instance().instantiate (*cpi);
} else {
@ -465,14 +473,20 @@ ARDOUR_UI::build_control_surface_menu ()
for (i = ControlProtocolManager::instance().control_protocol_info.begin(); i != ControlProtocolManager::instance().control_protocol_info.end(); ++i) {
string action_name = "Toggle";
action_name += (*i)->name;
action_name += legalize_for_path ((*i)->name);
action_name += "Surface";
string action_label = (*i)->name;
ActionManager::register_toggle_action (editor->editor_actions, action_name.c_str(), action_label.c_str(),
Glib::RefPtr<Action> act = ActionManager::register_toggle_action (editor->editor_actions, action_name.c_str(), action_label.c_str(),
(bind (mem_fun (*this, &ARDOUR_UI::toggle_control_protocol), *i)));
Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
if ((*i)->protocol || (*i)->requested) {
tact->set_active ();
}
ui += "<menuitem action='";
ui += action_name;
ui += "'/>\n";

View file

@ -2283,18 +2283,10 @@ Editor::set_state (const XMLNode& node)
Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
bool yn = (prop->value() == X_("yes"));
cerr << "at load time, show-editor-mixer = " << prop->value() << endl;
/* do it twice to force the change */
tact->set_active (!yn);
cerr << "now reset to " << yn << endl;
tact->set_active (yn);
cerr << "should be done\n";
}
}

View file

@ -36,8 +36,6 @@ Editor::editor_mixer_button_toggled ()
Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
if (act) {
Glib::RefPtr<Gtk::ToggleAction> tact = Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic(act);
bool yn = tact->get_active();
cerr << "button toggled, state = " << yn << endl;
show_editor_mixer (tact->get_active());
}
}

View file

@ -152,13 +152,13 @@ GainMeter::GainMeter (IO& io, Session& s)
used to describe meter points. In english, its "input".
*/
set_size_request_to_display_given_text (meter_point_button, _("tupni"), 2, 2);
set_size_request_to_display_given_text (meter_point_button, _("tupni"), 5, 5);
top_table.attach (meter_point_button, 1, 2, 0, 1);
}
gain_display_box.set_spacing (2);
set_size_request_to_display_given_text (gain_display_frame, "-86.0", 2, 2);
set_size_request_to_display_given_text (gain_display_frame, "-86.g", 2, 6); /* note the descender */
gain_display_frame.set_shadow_type (Gtk::SHADOW_IN);
gain_display_frame.set_name ("BaseFrame");
gain_display_frame.add (gain_display);
@ -166,7 +166,7 @@ GainMeter::GainMeter (IO& io, Session& s)
peak_display.set_name ("MixerStripPeakDisplay");
peak_display.add (peak_display_label);
set_size_request_to_display_given_text (peak_display_frame, "-86.0", 2, 2);
set_size_request_to_display_given_text (peak_display_frame, "-86.g", 2, 6); /* note the descender */
peak_display_frame.set_shadow_type (Gtk::SHADOW_IN);
peak_display_frame.set_name ("BaseFrame");
peak_display_frame.add (peak_display);

View file

@ -4,7 +4,7 @@
#include <gtkmm2ext/gtk_ui.h>
#define ENSURE_GUI_THREAD(slot) \
if (!Gtkmm2ext::UI::instance()->caller_is_gui_thread()) {\
if (!Gtkmm2ext::UI::instance()->caller_is_ui_thread()) {\
Gtkmm2ext::UI::instance()->call_slot ((slot));\
return;\
}

View file

@ -275,7 +275,7 @@ StreamView::remove_rec_region (Region *r)
{
ENSURE_GUI_THREAD(bind (mem_fun (*this, &StreamView::remove_rec_region), r));
if (!Gtkmm2ext::UI::instance()->caller_is_gui_thread()) {
if (!Gtkmm2ext::UI::instance()->caller_is_ui_thread()) {
fatal << "region deleted from non-GUI thread!" << endmsg;
/*NOTREACHED*/
}

View file

@ -29,6 +29,7 @@ audioregion.cc
auditioner.cc
automation.cc
automation_event.cc
basic_ui.cc
configuration.cc
connection.cc
control_protocol.cc
@ -99,6 +100,7 @@ if ardour['VST']:
ardour.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
ardour.Append(CXXFLAGS="-DDATA_DIR=\\\""+final_prefix+"/share\\\"")
ardour.Append(CXXFLAGS="-DMODULE_DIR=\\\""+final_prefix+"/lib\\\"")
ardour.Append(CXXFLAGS="-DCONFIG_DIR=\\\""+final_config_prefix+"\\\"")
ardour.Append(CXXFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"")

View file

@ -50,7 +50,8 @@ namespace ARDOUR {
std::string get_user_ardour_path ();
std::string get_system_ardour_path ();
std::string get_system_data_path ();
std::string get_system_module_path ();
std::string find_config_file (std::string name);
std::string find_data_file (std::string name, std::string subdir = "" );

View file

@ -0,0 +1,34 @@
#ifndef __ardour_basic_ui_h__
#define __ardour_basic_ui_h__
namespace ARDOUR {
class Session;
}
class BasicUI {
public:
BasicUI (ARDOUR::Session&);
virtual ~BasicUI ();
void loop_toggle ();
void goto_start ();
void goto_end ();
void add_marker ();
void rewind ();
void ffwd ();
void transport_stop ();
void transport_play ();
void rec_enable_toggle ();
void save_state ();
void prev_marker ();
void next_marker ();
void move_at (float speed);
void undo ();
void redo ();
void toggle_all_rec_enables ();
protected:
ARDOUR::Session& session;
};
#endif /* __ardour_basic_ui_h__ */

View file

@ -60,6 +60,8 @@ class Configuration : public Stateful
int set_state (const XMLNode&);
XMLNode& get_state (void);
XMLNode* control_protocol_state () { return _control_protocol_state; }
/* define accessor methods */
#undef CONFIG_VARIABLE
@ -88,6 +90,7 @@ class Configuration : public Stateful
#undef CONFIG_VARIABLE_SPECIAL
bool user_configuration;
XMLNode* _control_protocol_state;
XMLNode& state (bool user_only);
};

View file

@ -5,37 +5,25 @@
#include <list>
#include <sigc++/sigc++.h>
#include <ardour/basic_ui.h>
namespace ARDOUR {
class Route;
class Session;
class ControlProtocol : public sigc::trackable {
class ControlProtocol : public sigc::trackable, public BasicUI {
public:
ControlProtocol (Session&, std::string name);
virtual ~ControlProtocol();
virtual int init () { return 0; }
std::string name() const { return _name; }
virtual int set_active (bool yn) = 0;
bool get_active() const { return _active; }
sigc::signal<void> ActiveChanged;
enum SendWhat {
SendRoute,
SendGlobal
};
std::string name() const { return _name; }
void set_send (SendWhat);
void set_active (bool yn);
bool get_active() const { return active_thread > 0; }
bool send() const { return _send != 0; }
bool send_route_feedback () const { return _send & SendRoute; }
bool send_global_feedback () const { return _send & SendGlobal; }
virtual void send_route_feedback (std::list<Route*>&) {}
virtual void send_global_feedback () {}
/* signals that a control protocol can emit and other (presumably graphical)
user interfaces can respond to
@ -48,30 +36,8 @@ class ControlProtocol : public sigc::trackable {
static sigc::signal<void,float> ScrollTimeline;
protected:
ARDOUR::Session& session;
SendWhat _send;
std::string _name;
int active_thread;
int thread_request_pipe[2];
pthread_t _thread;
static void* _thread_work (void *);
void* thread_work ();
struct ThreadRequest {
enum Type {
Start,
Stop,
Quit
};
};
int init_thread();
int start_thread ();
int stop_thread ();
void terminate_thread ();
int poke_thread (ThreadRequest::Type);
bool _active;
};
extern "C" {

View file

@ -8,6 +8,8 @@
#include <pbd/lockmonitor.h>
#include <ardour/stateful.h>
namespace ARDOUR {
class ControlProtocol;
@ -19,9 +21,10 @@ struct ControlProtocolInfo {
ControlProtocol* protocol;
std::string name;
std::string path;
bool requested;
};
class ControlProtocolManager : public sigc::trackable
class ControlProtocolManager : public sigc::trackable, public Stateful
{
public:
ControlProtocolManager ();
@ -38,6 +41,11 @@ struct ControlProtocolInfo {
std::list<ControlProtocolInfo*> control_protocol_info;
static const std::string state_node_name;
int set_state (const XMLNode&);
XMLNode& get_state (void);
private:
static ControlProtocolManager* _instance;
@ -49,6 +57,7 @@ struct ControlProtocolInfo {
int control_protocol_discover (std::string path);
ControlProtocolDescriptor* get_descriptor (std::string path);
ControlProtocolInfo* cpi_by_name (std::string);
};
} // namespace

View file

@ -245,7 +245,7 @@ class Session : public sigc::trackable, public Stateful
std::string dead_sound_dir () const;
std::string automation_dir () const;
static string suffixed_search_path (std::string suffix);
static string suffixed_search_path (std::string suffix, bool data);
static string control_protocol_path ();
static string template_path ();
static string template_dir ();

189
libs/ardour/basic_ui.cc Normal file
View file

@ -0,0 +1,189 @@
/*
Copyright (C) 2006 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id$
*/
#include <ardour/basic_ui.h>
#include <ardour/session.h>
#include <ardour/location.h>
#include "i18n.h"
using namespace ARDOUR;
BasicUI::BasicUI (Session& s)
: session (s)
{
}
BasicUI::~BasicUI ()
{
}
void
BasicUI::loop_toggle ()
{
if (session.get_auto_loop()) {
session.request_auto_loop (false);
} else {
session.request_auto_loop (true);
if (!session.transport_rolling()) {
session.request_transport_speed (1.0);
}
}
}
void
BasicUI::goto_start ()
{
session.goto_start ();
}
void
BasicUI::goto_end ()
{
session.goto_end ();
}
void
BasicUI::add_marker ()
{
jack_nframes_t when = session.audible_frame();
session.locations()->add (new Location (when, when, _("unnamed"), Location::IsMark));
}
void
BasicUI::rewind ()
{
session.request_transport_speed (-2.0f);
}
void
BasicUI::ffwd ()
{
session.request_transport_speed (2.0f);
}
void
BasicUI::transport_stop ()
{
session.request_transport_speed (0.0);
}
void
BasicUI::transport_play ()
{
bool rolling = session.transport_rolling ();
if (session.get_auto_loop()) {
session.request_auto_loop (false);
}
if (session.get_play_range ()) {
session.request_play_range (false);
}
if (rolling) {
session.request_locate (session.last_transport_start(), true);
}
session.request_transport_speed (1.0f);
}
void
BasicUI::rec_enable_toggle ()
{
switch (session.record_status()) {
case Session::Disabled:
if (session.ntracks() == 0) {
// string txt = _("Please create 1 or more track\nbefore trying to record.\nCheck the Session menu.");
// MessageDialog msg (*editor, txt);
// msg.run ();
return;
}
session.maybe_enable_record ();
break;
case Session::Recording:
case Session::Enabled:
session.disable_record (true);
}
}
void
BasicUI::save_state ()
{
session.save_state ("");
}
void
BasicUI::prev_marker ()
{
Location *location = session.locations()->first_location_before (session.transport_frame());
if (location) {
session.request_locate (location->start(), session.transport_rolling());
} else {
session.goto_start ();
}
}
void
BasicUI::next_marker ()
{
Location *location = session.locations()->first_location_after (session.transport_frame());
if (location) {
session.request_locate (location->start(), session.transport_rolling());
} else {
session.request_locate (session.current_end_frame());
}
}
void
BasicUI::move_at (float speed)
{
session.request_transport_speed (speed);
}
void
BasicUI::undo ()
{
session.undo (1);
}
void
BasicUI::redo ()
{
session.redo (1);
}
void
BasicUI::toggle_all_rec_enables ()
{
if (session.get_record_enabled()) {
session.record_disenable_all ();
} else {
session.record_enable_all ();
}
}

View file

@ -28,6 +28,7 @@
#include <ardour/configuration.h>
#include <ardour/diskstream.h>
#include <ardour/destructive_filesource.h>
#include <ardour/control_protocol_manager.h>
#include "i18n.h"
@ -55,6 +56,7 @@ Configuration::Configuration ()
user_configuration (false)
{
_control_protocol_state = 0;
}
Configuration::~Configuration ()
@ -174,6 +176,8 @@ Configuration::state (bool user_only)
root->add_child_copy (*_extra_xml);
}
root->add_child_nocopy (ControlProtocolManager::instance().get_state());
return *root;
}
@ -221,6 +225,9 @@ Configuration::set_state (const XMLNode& root)
} else if (node->name() == "extra") {
_extra_xml = new XMLNode (*node);
} else if (node->name() == ControlProtocolManager::state_node_name) {
_control_protocol_state = new XMLNode (*node);
}
}

View file

@ -18,22 +18,11 @@
$Id$
*/
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <poll.h>
#include <pbd/pthread_utils.h>
#include <pbd/error.h>
#include <ardour/control_protocol.h>
#include <ardour/configuration.h>
#include <ardour/session.h>
using namespace ARDOUR;
using namespace std;
#include "i18n.h"
sigc::signal<void> ControlProtocol::ZoomToSession;
sigc::signal<void> ControlProtocol::ZoomOut;
sigc::signal<void> ControlProtocol::ZoomIn;
@ -41,212 +30,13 @@ sigc::signal<void> ControlProtocol::Enter;
sigc::signal<void,float> ControlProtocol::ScrollTimeline;
ControlProtocol::ControlProtocol (Session& s, string str)
: session (s),
: BasicUI (s),
_name (str)
{
active_thread = 1;
thread_request_pipe[0] = -1;
thread_request_pipe[1] = -1;
_active = false;
}
ControlProtocol::~ControlProtocol ()
{
terminate_thread ();
if (thread_request_pipe[0] >= 0) {
close (thread_request_pipe[0]);
close (thread_request_pipe[1]);
}
}
void
ControlProtocol::set_send (SendWhat sw)
{
_send = sw;
}
int
ControlProtocol::init_thread ()
{
if (pipe (thread_request_pipe) != 0) {
error << string_compose (_("%1: cannot create thread request pipe (%1)"), _name, strerror (errno))
<< endmsg;
return -1;
}
if (fcntl (thread_request_pipe[0], F_SETFL, O_NONBLOCK)) {
error << string_compose(_("%1: cannot set O_NONBLOCK on read pipe (%2)"), _name, strerror (errno)) << endmsg;
return -1;
}
if (fcntl (thread_request_pipe[1], F_SETFL, O_NONBLOCK)) {
error << string_compose(_("%1: cannot set O_NONBLOCK on signal write pipe (%2)"), _name, strerror (errno)) << endmsg;
return -1;
}
if (pthread_create_and_store ("tranzport delivery", &_thread, 0, _thread_work, this)) {
error << string_compose (_("%1: could not create thread"), _name) << endmsg;
return -1;
}
return 0;
}
int
ControlProtocol::poke_thread (ThreadRequest::Type why)
{
char c = (char) why;
return !(write (thread_request_pipe[1], &c, 1) == 1);
}
int
ControlProtocol::start_thread ()
{
return poke_thread (ThreadRequest::Start);
}
int
ControlProtocol::stop_thread ()
{
return poke_thread (ThreadRequest::Stop);
}
void
ControlProtocol::set_active (bool yn)
{
if (yn != active_thread) {
if (yn) {
/* make sure the feedback thread is alive */
start_thread ();
} else {
/* maybe put the feedback thread to sleep */
stop_thread ();
}
ActiveChanged ();
}
}
void
ControlProtocol::terminate_thread ()
{
void* status;
poke_thread (ThreadRequest::Quit);
pthread_join (_thread, &status);
}
void*
ControlProtocol::_thread_work (void* arg)
{
return static_cast<ControlProtocol*> (arg)->thread_work ();
}
void*
ControlProtocol::thread_work ()
{
PBD::ThreadCreated (pthread_self(), _name);
struct pollfd pfd[1];
int timeout;
struct sched_param rtparam;
int err;
cerr << _name << " receiver thread running\n";
memset (&rtparam, 0, sizeof (rtparam));
rtparam.sched_priority = 3; /* XXX should be relative to audio (JACK) thread */
if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
// do we care? not particularly.
info << string_compose (_("%1: delivery thread not running with realtime scheduling (%2)"), _name, strerror (errno)) << endmsg;
}
pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
if (active_thread) {
timeout = 10; // max (5, (int) Config->get_feedback_interval_ms());
} else {
timeout = -1;
}
while (1) {
pfd[0].fd = thread_request_pipe[0];
pfd[0].events = POLLIN|POLLHUP|POLLERR;
if (poll (pfd, 1, timeout) < 0) {
if (errno == EINTR) {
continue;
}
error << string_compose (_("Protocol \"%1\" thread: poll failed (%2)"), _name, strerror (errno))
<< endmsg;
break;
}
if (pfd[0].revents & ~POLLIN) {
error << string_compose (_("Error thread request pipe for protocol \"%1\""), _name) << endmsg;
break;
}
if (pfd[0].revents & POLLIN) {
char req;
/* empty the pipe of all current requests */
while (1) {
size_t nread = read (thread_request_pipe[0], &req, sizeof (req));
if (nread == 1) {
switch ((ThreadRequest::Type) req) {
case ThreadRequest::Start:
timeout = 10; // max (5, (int) Config->get_feedback_interval_ms());
active_thread++;
break;
case ThreadRequest::Stop:
timeout = -1;
if (active_thread) {
active_thread--;
}
break;
case ThreadRequest::Quit:
pthread_exit_pbd (0);
/*NOTREACHED*/
break;
default:
break;
}
} else if (nread == 0) {
break;
} else if (errno == EAGAIN) {
break;
} else {
fatal << string_compose (_("Error reading from thread request pipe for protocol \"%1\""), _name) << endmsg;
/*NOTREACHED*/
}
}
}
if (!active_thread) {
continue;
}
if (send_route_feedback ()) {
list<Route*> routes = session.get_routes(); /* copies the routes */
send_route_feedback (routes);
}
send_global_feedback ();
}
return 0;
}

View file

@ -15,6 +15,7 @@ using namespace std;
#include "i18n.h"
ControlProtocolManager* ControlProtocolManager::_instance = 0;
const string ControlProtocolManager::state_node_name = X_("ControlProtocols");
ControlProtocolManager::ControlProtocolManager ()
{
@ -42,6 +43,13 @@ ControlProtocolManager::set_session (Session& s)
{
_session = &s;
_session->going_away.connect (mem_fun (*this, &ControlProtocolManager::drop_session));
for (list<ControlProtocolInfo*>::iterator i = control_protocol_info.begin(); i != control_protocol_info.end(); ++i) {
if ((*i)->requested) {
instantiate (**i);
(*i)->requested = false;
}
}
}
void
@ -122,6 +130,8 @@ ControlProtocolManager::discover_control_protocols (string path)
vector<string *> *found;
PathScanner scanner;
cerr << "looking for control protocols in " << path << endl;
found = scanner (path, protocol_filter, 0, false, true);
for (vector<string*>::iterator i = found->begin(); i != found->end(); ++i) {
@ -145,9 +155,12 @@ ControlProtocolManager::control_protocol_discover (string path)
info->name = descriptor->name;
info->path = path;
info->protocol = 0;
info->requested = false;
control_protocol_info.push_back (info);
cerr << "discovered control surface protocol \"" << info->name << '"' << endl;
dlclose (descriptor->module);
}
@ -195,3 +208,59 @@ ControlProtocolManager::foreach_known_protocol (sigc::slot<void,const ControlPro
method (*i);
}
}
ControlProtocolInfo*
ControlProtocolManager::cpi_by_name (string name)
{
for (list<ControlProtocolInfo*>::iterator i = control_protocol_info.begin(); i != control_protocol_info.end(); ++i) {
if (name == (*i)->name) {
return *i;
}
}
return 0;
}
int
ControlProtocolManager::set_state (const XMLNode& node)
{
XMLNodeList clist;
XMLNodeConstIterator citer;
XMLProperty* prop;
clist = node.children();
for (citer = clist.begin(); citer != clist.end(); ++citer) {
if ((*citer)->name() == X_("Protocol")) {
if ((prop = (*citer)->property (X_("active"))) != 0) {
if (prop->value() == X_("yes")) {
if ((prop = (*citer)->property (X_("name"))) != 0) {
ControlProtocolInfo* cpi = cpi_by_name (prop->value());
if (cpi) {
if (_session) {
instantiate (*cpi);
} else {
cpi->requested = true;
}
}
}
}
}
}
}
}
XMLNode&
ControlProtocolManager::get_state (void)
{
XMLNode* root = new XMLNode (state_node_name);
LockMonitor lm (protocols_lock, __LINE__, __FILE__);
for (list<ControlProtocolInfo*>::iterator i = control_protocol_info.begin(); i != control_protocol_info.end(); ++i) {
XMLNode* child = new XMLNode (X_("Protocol"));
child->add_property (X_("name"), (*i)->name);
child->add_property (X_("active"), (*i)->protocol ? "yes" : "no");
root->add_child_nocopy (*child);
}
return *root;
}

View file

@ -270,6 +270,11 @@ ARDOUR::init (AudioEngine& engine, bool use_vst, bool try_optimization, void (*s
new ControlProtocolManager ();
ControlProtocolManager::instance().discover_control_protocols (Session::control_protocol_path());
XMLNode* node;
if ((node = Config->control_protocol_state()) != 0) {
ControlProtocolManager::instance().set_state (*node);
}
BoundsChanged = Change (StartChanged|PositionChanged|LengthChanged);
return 0;
@ -325,7 +330,7 @@ ARDOUR::get_user_ardour_path ()
}
string
ARDOUR::get_system_ardour_path ()
ARDOUR::get_system_data_path ()
{
string path;
@ -335,6 +340,17 @@ ARDOUR::get_system_ardour_path ()
return path;
}
string
ARDOUR::get_system_module_path ()
{
string path;
path += MODULE_DIR;
path += "/ardour2/";
return path;
}
static string
find_file (string name, string dir, string subdir = "")
{

View file

@ -2293,7 +2293,7 @@ Session::template_dir ()
}
string
Session::suffixed_search_path (string suffix)
Session::suffixed_search_path (string suffix, bool data)
{
string path;
@ -2301,7 +2301,12 @@ Session::suffixed_search_path (string suffix)
if (path[path.length()-1] != ':') {
path += ':';
}
path += get_system_ardour_path();
if (data) {
path += get_system_data_path();
} else {
path += get_system_module_path();
}
vector<string> split_path;
@ -2324,13 +2329,13 @@ Session::suffixed_search_path (string suffix)
string
Session::template_path ()
{
return suffixed_search_path (X_("templates"));
return suffixed_search_path (X_("templates"), true);
}
string
Session::control_protocol_path ()
{
return suffixed_search_path (X_("surfaces"));
return suffixed_search_path (X_("surfaces"), false);
}
int

View file

@ -946,6 +946,10 @@ Session::set_slave_source (SlaveSource src, jack_nframes_t frame)
bool reverse = false;
bool non_rt_required = false;
if (src == _slave_type) {
return 0;
}
if (_transport_speed) {
error << _("please stop the transport before adjusting slave settings") << endmsg;
/* help out non-MVC friendly UI's by telling them the slave type changed */
@ -953,10 +957,6 @@ Session::set_slave_source (SlaveSource src, jack_nframes_t frame)
return 0;
}
if (src == _slave_type) {
return 0;
}
// if (src == JACK && Config->get_jack_time_master()) {
// return -1;
// }

View file

@ -48,21 +48,23 @@ using std::map;
pthread_t UI::gui_thread;
UI *UI::theGtkUI = 0;
UI::UI (string name, int *argc, char ***argv, string rcfile)
: _ui_name (name)
BaseUI::RequestType Gtkmm2ext::ErrorMessage = BaseUI::new_request_type();
BaseUI::RequestType Gtkmm2ext::Quit = BaseUI::new_request_type();
BaseUI::RequestType Gtkmm2ext::TouchDisplay = BaseUI::new_request_type();
BaseUI::RequestType Gtkmm2ext::StateChange = BaseUI::new_request_type();
BaseUI::RequestType Gtkmm2ext::SetTip = BaseUI::new_request_type();
BaseUI::RequestType Gtkmm2ext::AddIdle = BaseUI::new_request_type();
BaseUI::RequestType Gtkmm2ext::AddTimeout = BaseUI::new_request_type();
#include <pbd/abstract_ui.cc> /* instantiate the template */
UI::UI (string namestr, int *argc, char ***argv, string rcfile)
: AbstractUI<UIRequest> (namestr, true)
{
theMain = new Main (argc, argv);
tips = new Tooltips;
if (pthread_key_create (&thread_request_buffer_key, 0)) {
cerr << _("cannot create thread request buffer key") << endl;
throw failed_constructor();
}
PBD::ThreadCreated.connect (mem_fun (*this, &UI::register_thread));
PBD::ThreadCreatedWithRequestSize.connect (mem_fun (*this, &UI::register_thread_with_request_count));
_ok = false;
_active = false;
if (!theGtkUI) {
@ -73,33 +75,39 @@ UI::UI (string name, int *argc, char ***argv, string rcfile)
/* NOTREACHED */
}
if (setup_signal_pipe ()) {
return;
}
/* add the pipe to the select/poll loop that GDK does */
gdk_input_add (signal_pipe[0],
GDK_INPUT_READ,
UI::signal_pipe_callback,
this);
errors = new TextViewer (850,100);
errors->text().set_editable (false);
errors->text().set_name ("ErrorText");
string title;
title = _ui_name;
title = namestr;
title += ": Log";
errors->set_title (title);
errors->dismiss_button().set_name ("ErrorLogCloseButton");
errors->signal_delete_event().connect (bind (ptr_fun (just_hide_it), (Window *) errors));
errors->signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), (Window *) errors));
register_thread (pthread_self(), X_("GUI"));
load_rcfile (rcfile);
_ok = true;
}
UI::~UI ()
{
close (signal_pipe[0]);
close (signal_pipe[1]);
}
bool
UI::caller_is_ui_thread ()
{
return pthread_equal (gui_thread, pthread_self());
}
int
@ -226,7 +234,13 @@ UI::kill ()
void
UI::quit ()
{
request (Quit);
UIRequest *req = get_request (Quit);
if (req == 0) {
return;
}
send_request (req);
}
static bool idle_quit ()
@ -241,16 +255,10 @@ UI::do_quit ()
Glib::signal_idle().connect (sigc::ptr_fun (idle_quit));
}
int
UI::set_quit_context()
{
return setjmp (quit_context);
}
void
UI::touch_display (Touchable *display)
{
Request *req = get_request (TouchDisplay);
UIRequest *req = get_request (TouchDisplay);
if (req == 0) {
return;
@ -261,52 +269,10 @@ UI::touch_display (Touchable *display)
send_request (req);
}
void
UI::call_slot (sigc::slot<void> slot)
{
Request *req = get_request (CallSlot);
if (req == 0) {
return;
}
req->slot = slot;
send_request (req);
}
void
UI::call_slot_locked (sigc::slot<void> slot)
{
if (caller_is_gui_thread()) {
call_slot (slot);
return;
}
Request *req = get_request (CallSlotLocked);
if (req == 0) {
return;
}
req->slot = slot;
pthread_mutex_init (&req->slot_lock, NULL);
pthread_cond_init (&req->slot_cond, NULL);
pthread_mutex_lock (&req->slot_lock);
send_request (req);
pthread_cond_wait (&req->slot_cond, &req->slot_lock);
pthread_mutex_unlock (&req->slot_lock);
delete req;
}
void
UI::set_tip (Widget *w, const gchar *tip, const gchar *hlp)
{
Request *req = get_request (SetTip);
UIRequest *req = get_request (SetTip);
if (req == 0) {
return;
@ -322,7 +288,7 @@ UI::set_tip (Widget *w, const gchar *tip, const gchar *hlp)
void
UI::set_state (Widget *w, StateType state)
{
Request *req = get_request (StateChange);
UIRequest *req = get_request (StateChange);
if (req == 0) {
return;
@ -337,7 +303,7 @@ UI::set_state (Widget *w, StateType state)
void
UI::idle_add (int (*func)(void *), void *arg)
{
Request *req = get_request (AddIdle);
UIRequest *req = get_request (AddIdle);
if (req == 0) {
return;
@ -349,124 +315,8 @@ UI::idle_add (int (*func)(void *), void *arg)
send_request (req);
}
void
UI::timeout_add (unsigned int timeout, int (*func)(void *), void *arg)
{
Request *req = get_request (AddTimeout);
if (req == 0) {
return;
}
req->function = func;
req->arg = arg;
req->timeout = timeout;
send_request (req);
}
/* END abstract_ui interfaces */
/* Handling requests */
void
UI::register_thread (pthread_t thread_id, string name)
{
register_thread_with_request_count (thread_id, name, 256);
}
void
UI::register_thread_with_request_count (pthread_t thread_id, string name, uint32_t num_requests)
{
RingBufferNPT<Request>* b = new RingBufferNPT<Request> (num_requests);
{
PBD::LockMonitor lm (request_buffer_map_lock, __LINE__, __FILE__);
request_buffers[thread_id] = b;
}
pthread_setspecific (thread_request_buffer_key, b);
}
UI::Request::Request()
{
}
UI::Request*
UI::get_request (RequestType rt)
{
RingBufferNPT<Request>* rbuf = static_cast<RingBufferNPT<Request>* >(pthread_getspecific (thread_request_buffer_key));
if (rbuf == 0) {
/* Cannot happen, but if it does we can't use the error reporting mechanism */
cerr << _("programming error: ")
<< string_compose (X_("no GUI request buffer found for thread %1"), pthread_self())
<< endl;
abort ();
}
RingBufferNPT<Request>::rw_vector vec;
rbuf->get_write_vector (&vec);
if (vec.len[0] == 0) {
if (vec.len[1] == 0) {
cerr << string_compose (X_("no space in GUI request buffer for thread %1"), pthread_self())
<< endl;
return 0;
} else {
vec.buf[1]->type = rt;
return vec.buf[1];
}
} else {
vec.buf[0]->type = rt;
return vec.buf[0];
}
}
int
UI::setup_signal_pipe ()
{
/* setup the pipe that other threads send us notifications/requests
through.
*/
if (pipe (signal_pipe)) {
error << "UI: cannot create error signal pipe ("
<< std::strerror (errno) << ")"
<< endmsg;
return -1;
}
if (fcntl (signal_pipe[0], F_SETFL, O_NONBLOCK)) {
error << "UI: cannot set O_NONBLOCK on "
"signal read pipe ("
<< std::strerror (errno) << ")"
<< endmsg;
return -1;
}
if (fcntl (signal_pipe[1], F_SETFL, O_NONBLOCK)) {
error << "UI: cannot set O_NONBLOCK on "
"signal write pipe ("
<< std::strerror (errno)
<< ")"
<< endmsg;
return -1;
}
/* add the pipe to the select/poll loop that GDK does */
gdk_input_add (signal_pipe[0],
GDK_INPUT_READ,
UI::signal_pipe_callback,
this);
return 0;
}
void
UI::signal_pipe_callback (void *arg, int fd, GdkInputCondition cond)
{
@ -480,147 +330,45 @@ UI::signal_pipe_callback (void *arg, int fd, GdkInputCondition cond)
}
void
UI::handle_ui_requests ()
UI::do_request (UIRequest* req)
{
RequestBufferMap::iterator i;
if (req->type == ErrorMessage) {
request_buffer_map_lock.lock ();
for (i = request_buffers.begin(); i != request_buffers.end(); ++i) {
RingBufferNPT<Request>::rw_vector vec;
while (true) {
/* we must process requests 1 by 1 because
the request may run a recursive main
event loop that will itself call
handle_ui_requests. when we return
from the request handler, we cannot
expect that the state of queued requests
is even remotely consistent with
the condition before we called it.
*/
i->second->get_read_vector (&vec);
if (vec.len[0] == 0) {
break;
} else {
/* copy constructor does a deep
copy of the Request object,
unlike Ringbuffer::read()
*/
Request req (*vec.buf[0]);
i->second->increment_read_ptr (1);
request_buffer_map_lock.unlock ();
do_request (&req);
request_buffer_map_lock.lock ();
}
}
}
request_buffer_map_lock.unlock ();
}
void
UI::do_request (Request* req)
{
switch (req->type) {
case ErrorMessage:
process_error_message (req->chn, req->msg);
free (const_cast<char*>(req->msg)); /* it was strdup'ed */
req->msg = 0; /* don't free it again in the destructor */
break;
case Quit:
} else if (req->type == Quit) {
do_quit ();
break;
case CallSlot:
} else if (req->type == CallSlot) {
req->slot ();
break;
case CallSlotLocked:
pthread_mutex_lock (&req->slot_lock);
req->slot ();
pthread_cond_signal (&req->slot_cond);
pthread_mutex_unlock (&req->slot_lock);
break;
} else if (req->type == TouchDisplay) {
case TouchDisplay:
req->display->touch ();
if (req->display->delete_after_touch()) {
delete req->display;
}
break;
case StateChange:
} else if (req->type == StateChange) {
req->widget->set_state (req->new_state);
break;
case SetTip:
} else if (req->type == SetTip) {
/* XXX need to figure out how this works */
break;
case AddIdle:
gtk_idle_add (req->function, req->arg);
break;
} else {
case AddTimeout:
gtk_timeout_add (req->timeout, req->function, req->arg);
break;
default:
error << "UI: unknown request type "
error << "GtkUI: unknown request type "
<< (int) req->type
<< endmsg;
}
}
void
UI::send_request (Request *req)
{
if (instance() == 0) {
return; /* XXX is this the right thing to do ? */
}
if (caller_is_gui_thread()) {
// cerr << "GUI thread sent request " << req << " type = " << req->type << endl;
do_request (req);
} else {
const char c = 0;
RingBufferNPT<Request*>* rbuf = static_cast<RingBufferNPT<Request*> *> (pthread_getspecific (thread_request_buffer_key));
if (rbuf == 0) {
/* can't use the error system to report this, because this
thread isn't registered!
*/
cerr << _("programming error: ")
<< string_compose (X_("UI::send_request() called from %1, but no request buffer exists for that thread"),
pthread_self())
<< endl;
abort ();
}
// cerr << "thread " << pthread_self() << " sent request " << req << " type = " << req->type << endl;
rbuf->increment_write_ptr (1);
write (signal_pipe[1], &c, 1);
}
}
void
UI::request (RequestType rt)
{
Request *req = get_request (rt);
if (req == 0) {
return;
}
send_request (req);
}
/*======================================================================
Error Display
======================================================================*/
@ -628,10 +376,10 @@ UI::request (RequestType rt)
void
UI::receive (Transmitter::Channel chn, const char *str)
{
if (caller_is_gui_thread()) {
if (caller_is_ui_thread()) {
process_error_message (chn, str);
} else {
Request* req = get_request (ErrorMessage);
UIRequest* req = get_request (ErrorMessage);
if (req == 0) {
return;
@ -763,7 +511,7 @@ UI::handle_fatal (const char *message)
win.set_default_size (400, 100);
string title;
title = _ui_name;
title = name();
title += ": Fatal Error";
win.set_title (title);
@ -787,7 +535,7 @@ UI::popup_error (const char *text)
{
PopUp *pup;
if (!caller_is_gui_thread()) {
if (!caller_is_ui_thread()) {
error << "non-UI threads can't use UI::popup_error"
<< endmsg;
return;
@ -802,7 +550,7 @@ UI::popup_error (const char *text)
void
UI::flush_pending ()
{
if (!caller_is_gui_thread()) {
if (!caller_is_ui_thread()) {
error << "non-UI threads cannot call UI::flush_pending()"
<< endmsg;
return;

View file

@ -39,6 +39,7 @@
#include <pbd/atomic.h>
#include <pbd/pool.h>
#include <pbd/error.h>
#include <pbd/receiver.h>
#include <pbd/lockmonitor.h>
using std::string;
@ -50,8 +51,42 @@ namespace Gtkmm2ext {
class TextViewer;
class UI : public AbstractUI
extern BaseUI::RequestType ErrorMessage;
extern BaseUI::RequestType Quit;
extern BaseUI::RequestType CallSlot;
extern BaseUI::RequestType TouchDisplay;
extern BaseUI::RequestType StateChange;
extern BaseUI::RequestType SetTip;
extern BaseUI::RequestType AddIdle;
extern BaseUI::RequestType AddTimeout;
struct UIRequest : public BaseUI::BaseRequestObject {
/* this once used anonymous unions to merge elements
that are never part of the same request. that makes
the creation of a legal copy constructor difficult
because of the semantics of the slot member.
*/
Touchable *display;
const char *msg;
Gtk::StateType new_state;
int (*function)(void *);
Gtk::Widget *widget;
Transmitter::Channel chn;
void *arg;
const char *msg2;
sigc::slot<void> slot;
~UIRequest () {
if (type == ErrorMessage && msg) {
/* msg was strdup()'ed */
free ((char *)msg);
}
}
};
class UI : public Receiver, public AbstractUI<UIRequest>
{
public:
UI (string name, int *argc, char **argv[], string rcfile);
@ -59,36 +94,29 @@ class UI : public AbstractUI
static UI *instance() { return theGtkUI; }
/* receiver interface */
void receive (Transmitter::Channel, const char *);
/* Abstract UI interfaces */
bool caller_is_ui_thread ();
/* Gtk-UI specific interfaces */
bool running ();
void quit ();
void kill ();
int load_rcfile (string);
void request (RequestType);
void run (Receiver &old_receiver);
void call_slot (sigc::slot<void>);
void call_slot_locked (sigc::slot<void>);
void touch_display (Touchable *);
void receive (Transmitter::Channel, const char *);
void register_thread (pthread_t, string);
void register_thread_with_request_count (pthread_t, string, uint32_t num_requests);
bool caller_is_gui_thread () {
return pthread_equal (gui_thread, pthread_self());
}
/* Gtk-UI specific interfaces */
int set_quit_context ();
void set_tip (Gtk::Widget *, const gchar *txt, const gchar *hlp = 0);
void set_state (Gtk::Widget *w, Gtk::StateType state);
void idle_add (int (*)(void *), void *);
void timeout_add (unsigned int, int (*)(void *), void *);
void popup_error (const char *text);
void flush_pending ();
void toggle_errors ();
void touch_display (Touchable *);
void set_tip (Gtk::Widget *w, const gchar *tip, const gchar *hlp);
void idle_add (int (*func)(void *), void *arg);
template<class T> static bool idle_delete (T *obj) { delete obj; return false; }
template<class T> static void delete_when_idle (T *obj) {
@ -106,6 +134,8 @@ class UI : public AbstractUI
static bool just_hide_it (GdkEventAny *, Gtk::Window *);
static pthread_t the_gui_thread() { return gui_thread; }
protected:
virtual void handle_fatal (const char *);
virtual void display_message (const char *prefix, gint prefix_len,
@ -113,58 +143,10 @@ class UI : public AbstractUI
Glib::RefPtr<Gtk::TextBuffer::Tag> mtag,
const char *msg);
/* stuff to invoke member functions in another
thread so that we can keep the GUI running.
*/
template<class UI_CLASS> struct thread_arg {
UI_CLASS *ui;
void (UI_CLASS::*func)(void *);
void *arg;
};
template<class UI_CLASS> static void *start_other_thread (void *arg);
template<class UI_CLASS> void other_thread (void (UI_CLASS::*func)(void *), void *arg = 0);
private:
struct Request {
/* this once used anonymous unions to merge elements
that are never part of the same request. that makes
the creation of a legal copy constructor difficult
because of the semantics of the slot member.
*/
RequestType type;
Touchable *display;
const char *msg;
Gtk::StateType new_state;
int (*function)(void *);
Gtk::Widget *widget;
Transmitter::Channel chn;
void *arg;
const char *msg2;
unsigned int timeout;
sigc::slot<void> slot;
/* this is for CallSlotLocked requests */
pthread_mutex_t slot_lock;
pthread_cond_t slot_cond;
Request ();
~Request () {
if (type == ErrorMessage && msg) {
/* msg was strdup()'ed */
free ((char *)msg);
}
}
};
static UI *theGtkUI;
static pthread_t gui_thread;
bool _active;
string _ui_name;
Gtk::Main *theMain;
Gtk::Tooltips *tips;
TextViewer *errors;
@ -177,18 +159,6 @@ class UI : public AbstractUI
Glib::RefPtr<Gtk::TextBuffer::Tag> warning_ptag;
Glib::RefPtr<Gtk::TextBuffer::Tag> warning_mtag;
int signal_pipe[2];
PBD::Lock request_buffer_map_lock;
typedef std::map<pthread_t,RingBufferNPT<Request>* > RequestBufferMap;
RequestBufferMap request_buffers;
Request* get_request(RequestType);
pthread_key_t thread_request_buffer_key;
int setup_signal_pipe ();
void handle_ui_requests ();
void do_request (Request *);
void send_request (Request *);
static void signal_pipe_callback (void *, gint, GdkInputCondition);
void process_error_message (Transmitter::Channel, const char *);
void do_quit ();
@ -197,38 +167,9 @@ class UI : public AbstractUI
bool color_selection_deleted (GdkEventAny *);
bool color_picked;
jmp_buf quit_context;
void do_request (UIRequest*);
};
template<class UI_CLASS> void *
UI::start_other_thread (void *arg)
{
thread_arg<UI_CLASS> *ta = (thread_arg<UI_CLASS> *) arg;
(ta->ui->*ta->func)(ta->arg);
delete ta;
return 0;
}
template<class UI_CLASS> void
UI::other_thread (void (UI_CLASS::*func)(void *), void *arg)
{
pthread_t thread_id;
thread_arg<UI_CLASS> *ta = new thread_arg<UI_CLASS>;
ta->ui = dynamic_cast<UI_CLASS *> (this);
if (ta->ui == 0) {
error << "UI::other thread called illegally"
<< endmsg;
return;
}
ta->func = func;
ta->arg = arg;
pthread_create (&thread_id, 0, start_other_thread, ta);
}
} /* namespace */
#endif /* __pbd_gtk_ui_h__ */

View file

@ -2,15 +2,22 @@
import glob
Import('env libraries')
Import('env libraries i18n')
pbd3 = env.Copy()
domain = 'libpbd'
pbd3.Append(DOMAIN=domain,MAJOR=3,MINOR=2,MICRO=0)
pbd3.Append(CXXFLAGS="-DPACKAGE=\\\"" + domain + "\\\"")
pbd3.Append(CXXFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
pbd3.Append(CXXFLAGS="-DLIBSIGC_DISABLE_DEPRECATED")
pbd3.Append(PACKAGE=domain)
pbd3.Append(POTFILE=domain + '.pot')
pbd3_files = Split("""
basename.cc
base_ui.cc
dirname.cc
dmalloc.cc
mountpoint.cc
@ -38,8 +45,6 @@ pbd3 = conf.Finish()
pbd3.Merge ([ libraries['sigc2'], libraries['xml'] ])
pbd3.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
pbd3.Append(CCFLAGS="-DLIBSIGC_DISABLE_DEPRECATED")
pbd3.VersionBuild(['version.cc','pbd/version.h'], 'SConscript')
@ -50,5 +55,11 @@ else:
Default(libpbd3)
if env['NLS']:
i18n (pbd3, pbd3_files, env)
env.Alias('tarball', env.Distribute (env['DISTTREE'],
[ 'SConscript' ] + pbd3_files + glob.glob('pbd/*.h')))
[ 'SConscript' ] +
pbd3_files +
glob.glob('po/*.po') +
glob.glob('pbd/*.h')))

87
libs/pbd3/base_ui.cc Normal file
View file

@ -0,0 +1,87 @@
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <pbd/base_ui.h>
#include <pbd/error.h>
#include <pbd/compose.h>
#include <pbd/failed_constructor.h>
#include "i18n.h"
using namespace std;
uint32_t BaseUI::rt_bit = 1;
BaseUI::RequestType BaseUI::CallSlot = BaseUI::new_request_type();
BaseUI::BaseUI (string str, bool with_signal_pipe)
: _name (str)
{
/* odd pseudo-singleton semantics */
base_ui_instance = this;
signal_pipe[0] = -1;
signal_pipe[1] = -1;
if (with_signal_pipe) {
if (setup_signal_pipe ()) {
throw failed_constructor ();
}
}
}
BaseUI::~BaseUI()
{
if (signal_pipe[0] >= 0) {
close (signal_pipe[0]);
}
if (signal_pipe[1] >= 0) {
close (signal_pipe[1]);
}
}
BaseUI::RequestType
BaseUI::new_request_type ()
{
RequestType rt;
/* XXX catch out-of-range */
rt = RequestType (rt_bit);
rt_bit <<= 1;
return rt;
}
int
BaseUI::setup_signal_pipe ()
{
/* setup the pipe that other threads send us notifications/requests
through.
*/
if (pipe (signal_pipe)) {
error << string_compose (_("%1-UI: cannot create error signal pipe (%2)"), _name, std::strerror (errno))
<< endmsg;
return -1;
}
if (fcntl (signal_pipe[0], F_SETFL, O_NONBLOCK)) {
error << string_compose (_("%1-UI: cannot set O_NONBLOCK on signal read pipe (%2)"), _name, std::strerror (errno))
<< endmsg;
return -1;
}
if (fcntl (signal_pipe[1], F_SETFL, O_NONBLOCK)) {
error << string_compose (_("%1-UI: cannot set O_NONBLOCK on signal write pipe (%2)"), _name, std::strerror (errno))
<< endmsg;
return -1;
}
return 0;
}

View file

@ -21,47 +21,58 @@
#ifndef __pbd_abstract_ui_h__
#define __pbd_abstract_ui_h__
#include <pbd/receiver.h>
#include <map>
#include <string>
#include <pthread.h>
#include <sigc++/sigc++.h>
#include <pbd/receiver.h>
#include <pbd/lockmonitor.h>
#include <pbd/ringbufferNPT.h>
#include <pbd/base_ui.h>
class Touchable;
class AbstractUI : public Receiver
template <class RequestObject>
class AbstractUI : public BaseUI
{
public:
enum RequestType {
ErrorMessage,
Quit,
CallSlot,
CallSlotLocked,
TouchDisplay,
StateChange,
SetTip,
AddIdle,
AddTimeout,
};
bool ok() { return _ok; }
AbstractUI () {}
AbstractUI (std::string name, bool with_signal_pipe);
virtual ~AbstractUI() {}
virtual void run (Receiver &old_receiver) = 0;
virtual void quit () = 0;
virtual bool running () = 0;
virtual void request (RequestType) = 0;
virtual void touch_display (Touchable *) = 0;
virtual void call_slot (sigc::slot<void>) = 0;
virtual bool caller_is_gui_thread() = 0;
virtual bool caller_is_ui_thread() = 0;
/* needed to be a receiver ... */
void call_slot (sigc::slot<void> el_slot) {
RequestObject *req = get_request (BaseUI::CallSlot);
virtual void receive (Transmitter::Channel, const char *) = 0;
if (req == 0) {
return;
}
req->slot = el_slot;
send_request (req);
}
void register_thread (pthread_t, std::string);
void register_thread_with_request_count (pthread_t, std::string, uint32_t num_requests);
protected:
bool _ok;
typedef RingBufferNPT<RequestObject> RequestBuffer;
typedef typename RequestBuffer::rw_vector RequestBufferVector;
typedef typename std::map<pthread_t,RequestBuffer*>::iterator RequestBufferMapIterator;
PBD::Lock request_buffer_map_lock;
typedef std::map<pthread_t,RequestBuffer*> RequestBufferMap;
RequestBufferMap request_buffers;
pthread_key_t thread_request_buffer_key;
RequestObject* get_request (RequestType);
void handle_ui_requests ();
void send_request (RequestObject *);
virtual void do_request (RequestObject *) = 0;
};
#endif // __pbd_abstract_ui_h__
#endif /* __pbd_abstract_ui_h__ */

46
libs/pbd3/pbd/base_ui.h Normal file
View file

@ -0,0 +1,46 @@
#ifndef __pbd_base_ui_h__
#define __pbd_base_ui_h__
#include <string>
#include <stdint.h>
#include <sigc++/slot.h>
#include <sigc++/trackable.h>
class BaseUI : virtual public sigc::trackable {
public:
BaseUI (std::string name, bool with_signal_pipes);
virtual ~BaseUI();
BaseUI* base_instance() { return base_ui_instance; }
std::string name() const { return _name; }
bool ok() const { return _ok; }
enum RequestType {
range_guarantee = ~0
};
struct BaseRequestObject {
RequestType type;
sigc::slot<void> the_slot;
};
static RequestType new_request_type();
static RequestType BaseUI::CallSlot;
protected:
int signal_pipe[2];
bool _ok;
private:
std::string _name;
BaseUI* base_ui_instance;
static uint32_t rt_bit;
int setup_signal_pipe ();
};
#endif /* __pbd_base_ui_h__ */

View file

@ -31,7 +31,7 @@ using std::vector;
class strstream;
class Receiver : public sigc::trackable
class Receiver : virtual public sigc::trackable
{
public:
Receiver ();

View file

@ -24,6 +24,8 @@
#include <sys/mman.h>
#include <pbd/atomic.h>
/* ringbuffer class where the element size is not required to be a power of two */
template<class T>
class RingBufferNPT
{

View file

@ -46,6 +46,8 @@ Default(libardour_genericmidi)
if env['NLS']:
i18n (genericmidi, genericmidi_files, env)
env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2/surfaces'), libardour_genericmidi))
env.Alias('tarball', env.Distribute (env['DISTTREE'],
[ 'SConscript', 'i18n.h', 'gettext.h' ] +
genericmidi_files +

View file

@ -22,10 +22,10 @@ GenericMidiControlProtocol::~GenericMidiControlProtocol ()
}
int
GenericMidiControlProtocol::init ()
GenericMidiControlProtocol::set_active (bool yn)
{
/* start delivery/outbound thread */
return init_thread ();
return 0;
}
void
@ -63,9 +63,3 @@ GenericMidiControlProtocol::send_route_feedback (list<Route*>& routes)
}
}
bool
GenericMidiControlProtocol::active() const
{
return _port && send();
}

View file

@ -14,9 +14,7 @@ class GenericMidiControlProtocol : public ControlProtocol {
GenericMidiControlProtocol (Session&);
virtual ~GenericMidiControlProtocol();
int init ();
bool active() const;
int set_active (bool yn);
void set_port (MIDI::Port*);
MIDI::Port* port () const { return _port; }

View file

@ -9,7 +9,7 @@ new_generic_midi_protocol (ControlProtocolDescriptor* descriptor, Session* s)
{
GenericMidiControlProtocol* gmcp = new GenericMidiControlProtocol (*s);
if (gmcp->init ()) {
if (gmcp->set_active (true)) {
delete gmcp;
return 0;
}

View file

@ -46,6 +46,8 @@ Default(libardour_tranzport)
if env['NLS']:
i18n (tranzport, tranzport_files, env)
env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2/surfaces'), libardour_tranzport))
env.Alias('tarball', env.Distribute (env['DISTTREE'],
[ 'SConscript', 'i18n.h', 'gettext.h' ] +
tranzport_files +

View file

@ -9,7 +9,7 @@ new_tranzport_protocol (ControlProtocolDescriptor* descriptor, Session* s)
{
TranzportControlProtocol* tcp = new TranzportControlProtocol (*s);
if (tcp->init ()) {
if (tcp->set_active (true)) {
delete tcp;
return 0;
}

View file

@ -1,7 +1,29 @@
/*
Copyright (C) 2006 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id$
*/
#include <iostream>
#include <algorithm>
#include <float.h>
#include <sys/time.h>
#include <errno.h>
#include <pbd/pthread_utils.h>
@ -19,8 +41,16 @@ using namespace sigc;
#include "i18n.h"
#include <pbd/abstract_ui.cc>
BaseUI::RequestType LEDChange = BaseUI::new_request_type ();
BaseUI::RequestType Print = BaseUI::new_request_type ();
BaseUI::RequestType SetCurrentTrack = BaseUI::new_request_type ();
TranzportControlProtocol::TranzportControlProtocol (Session& s)
: ControlProtocol (s, _("Tranzport"))
: ControlProtocol (s, X_("Tranzport")),
AbstractUI<TranzportRequest> (X_("Tranzport"), false)
{
timeout = 60000;
buttonmask = 0;
@ -34,103 +64,90 @@ TranzportControlProtocol::TranzportControlProtocol (Session& s)
wheel_shift_mode = WheelShiftGain;
timerclear (&last_wheel_motion);
last_wheel_dir = 1;
last_track_gain = FLT_MAX;
display_mode = DisplayNormal;
requested_display_mode = display_mode;
memset (current_screen, 0, sizeof (current_screen));
memset (pending_screen, 0, sizeof (pending_screen));
for (uint32_t i = 0; i < sizeof(lights)/sizeof(lights[0]); ++i) {
lights[i] = false;
}
session.RecordStateChanged.connect (mem_fun (*this, &TranzportControlProtocol::record_status_changed));
for (uint32_t i = 0; i < sizeof(pending_lights)/sizeof(pending_lights[0]); ++i) {
pending_lights[i] = false;
}
}
TranzportControlProtocol::~TranzportControlProtocol ()
{
if (udev) {
pthread_cancel_one (thread);
lcd_clear ();
close ();
}
set_active (false);
}
int
TranzportControlProtocol::init ()
TranzportControlProtocol::set_active (bool yn)
{
if (yn != _active) {
if (yn) {
if (open ()) {
return -1;
}
if (pthread_create_and_store (X_("tranzport monitor"), &thread, 0, _monitor_work, this) == 0) {
_active = true;
} else {
return -1;
}
} else {
pthread_cancel_one (thread);
lcd_clear ();
lights_off ();
show_wheel_mode();
next_track ();
show_transport_time ();
/* outbound thread */
init_thread ();
/* inbound thread */
pthread_create_and_store (X_("tranzport monitor"), &thread, 0, _monitor_work, this);
close ();
_active = false;
}
}
return 0;
}
bool
TranzportControlProtocol::active() const
{
return true;
}
void
TranzportControlProtocol::send_route_feedback (list<Route*>& routes)
TranzportControlProtocol::show_track_gain ()
{
}
void
TranzportControlProtocol::send_global_feedback ()
{
if (requested_display_mode != display_mode) {
switch (requested_display_mode) {
case DisplayNormal:
enter_normal_display_mode ();
break;
case DisplayBigMeter:
enter_big_meter_mode ();
break;
if (current_route) {
gain_t g = current_route->gain();
if (g != last_track_gain) {
char buf[16];
snprintf (buf, sizeof (buf), "%6.1fdB", coefficient_to_dB (current_route->gain()));
print (0, 9, buf);
last_track_gain = g;
}
}
switch (display_mode) {
case DisplayBigMeter:
show_meter ();
break;
case DisplayNormal:
show_transport_time ();
if (session.soloing()) {
light_on (LightAnysolo);
} else {
light_off (LightAnysolo);
}
break;
print (0, 9, " ");
}
}
void
TranzportControlProtocol::normal_update ()
{
show_current_track ();
show_transport_time ();
show_track_gain ();
show_wheel_mode ();
}
void
TranzportControlProtocol::next_display_mode ()
{
switch (display_mode) {
case DisplayNormal:
requested_display_mode = DisplayBigMeter;
display_mode = DisplayBigMeter;
break;
case DisplayBigMeter:
requested_display_mode = DisplayNormal;
display_mode = DisplayNormal;
break;
}
}
@ -147,11 +164,14 @@ TranzportControlProtocol::enter_big_meter_mode ()
void
TranzportControlProtocol::enter_normal_display_mode ()
{
last_where += 1; /* force time redisplay */
last_track_gain = FLT_MAX; /* force gain redisplay */
lcd_clear ();
lights_off ();
show_current_track ();
show_wheel_mode ();
last_where += 1; /* force time redisplay */
show_wheel_mode ();
show_transport_time ();
display_mode = DisplayNormal;
}
@ -205,7 +225,7 @@ TranzportControlProtocol::show_meter ()
uint32_t fill = (uint32_t) floor (fraction * 40);
char buf[21];
int i;
uint32_t i;
if (fill == last_meter_fill) {
/* nothing to do */
@ -286,20 +306,6 @@ TranzportControlProtocol::_monitor_work (void* arg)
return static_cast<TranzportControlProtocol*>(arg)->monitor_work ();
}
void*
TranzportControlProtocol::monitor_work ()
{
PBD::ThreadCreated (pthread_self(), X_("tranzport monitor"));
while (true) {
if (read ()) {
break;
}
}
return 0;
}
int
TranzportControlProtocol::open ()
{
@ -366,6 +372,7 @@ TranzportControlProtocol::close ()
if (usb_close (udev)) {
error << _("Tranzport: cannot close device") << endmsg;
udev = 0;
ret = 0;
}
@ -377,10 +384,7 @@ TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
{
int val;
{
LockMonitor lm (write_lock, __LINE__, __FILE__);
val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout);
}
if (val < 0)
return val;
@ -405,29 +409,56 @@ TranzportControlProtocol::lcd_clear ()
cmd[6] = ' ';
cmd[7] = 0x00;
{
LockMonitor lp (print_lock, __LINE__, __FILE__);
LockMonitor lw (write_lock, __LINE__, __FILE__);
for (uint8_t i = 0; i < 10; ++i) {
cmd[2] = i;
usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, 500);
}
memset (current_screen, ' ', sizeof (current_screen));
}
memset (pending_screen, ' ', sizeof (pending_screen));
}
void
TranzportControlProtocol::lights_off ()
{
light_off (LightRecord);
light_off (LightTrackrec);
light_off (LightTrackmute);
light_off (LightTracksolo);
light_off (LightAnysolo);
light_off (LightLoop);
light_off (LightPunch);
uint8_t cmd[8];
cmd[0] = 0x00;
cmd[1] = 0x00;
cmd[3] = 0x00;
cmd[4] = 0x00;
cmd[5] = 0x00;
cmd[6] = 0x00;
cmd[7] = 0x00;
cmd[2] = LightRecord;
if (write (cmd, 500) == 0) {
lights[LightRecord] = false;
}
cmd[2] = LightTrackrec;
if (write (cmd, 500) == 0) {
lights[LightTrackrec] = false;
}
cmd[2] = LightTrackmute;
if (write (cmd, 500) == 0) {
lights[LightTrackmute] = false;
}
cmd[2] = LightTracksolo;
if (write (cmd, 500) == 0) {
lights[LightTracksolo] = false;
}
cmd[2] = LightAnysolo;
if (write (cmd, 500) == 0) {
lights[LightAnysolo] = false;
}
cmd[2] = LightLoop;
if (write (cmd, 500) == 0) {
lights[LightLoop] = false;
}
cmd[2] = LightPunch;
if (write (cmd, 500) == 0) {
lights[LightPunch] = false;
}
}
int
@ -486,25 +517,250 @@ TranzportControlProtocol::light_off (LightID light)
}
}
int
TranzportControlProtocol::read (uint32_t timeout_override)
void*
TranzportControlProtocol::monitor_work ()
{
struct sched_param rtparam;
int err;
uint8_t buf[8];
int val;
memset(buf, 0, 8);
again:
val = usb_interrupt_read(udev, READ_ENDPOINT, (char*) buf, 8, timeout_override ? timeout_override : timeout);
if (val < 0) {
return val;
}
if (val != 8) {
if (val == 0) {
goto again;
}
return -1;
PBD::ThreadCreated (pthread_self(), X_("Tranzport"));
memset (&rtparam, 0, sizeof (rtparam));
rtparam.sched_priority = 3; /* XXX should be relative to audio (JACK) thread */
if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
// do we care? not particularly.
info << string_compose (_("%1: thread not running with realtime scheduling (%2)"), BaseUI::name(), strerror (errno)) << endmsg;
}
pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
/* set initial state */
lcd_clear ();
lights_off ();
show_wheel_mode();
next_track ();
show_transport_time ();
while (true) {
/* bInterval for this beastie is 10ms */
pthread_testcancel();
usleep (20000);
pthread_testcancel();
/* anything to read ? */
val = usb_interrupt_read (udev, READ_ENDPOINT, (char*) buf, 8, 0);
/* any requests to handle? */
handle_ui_requests ();
if (val == 8) {
process (buf);
}
/* update whatever needs updating */
update_state ();
}
return (void*) 0;
}
int
TranzportControlProtocol::update_state ()
{
int row;
int col_base;
int col;
int cell;
/* do the text updates */
switch (display_mode) {
case DisplayBigMeter:
show_meter ();
break;
case DisplayNormal:
normal_update ();
break;
}
/* next: flush LCD */
cell = 0;
for (row = 0; row < 2; ++row) {
for (col_base = 0, col = 0; col < 20; ) {
if (pending_screen[row][col] != current_screen[row][col]) {
/* something in this cell is different, so dump the cell
to the device.
*/
uint8_t cmd[8];
cmd[0] = 0x00;
cmd[1] = 0x01;
cmd[2] = cell;
cmd[3] = pending_screen[row][col_base];
cmd[4] = pending_screen[row][col_base+1];
cmd[5] = pending_screen[row][col_base+2];
cmd[6] = pending_screen[row][col_base+3];
cmd[7] = 0x00;
if (usb_interrupt_write (udev, WRITE_ENDPOINT, (char *) cmd, 8, 500) == 8) {
/* successful write: copy to current */
memcpy (&current_screen[row][col_base], &pending_screen[row][col_base], 4);
}
/* skip the rest of the 4 character cell since we wrote+copied it already */
col_base += 4;
col = col_base;
cell++;
} else {
col++;
if (col && col % 4 == 0) {
cell++;
col_base += 4;
}
}
}
}
/* now update LED's */
/* per track */
if (current_route) {
AudioTrack* at = dynamic_cast<AudioTrack*> (current_route);
if (at && at->record_enabled()) {
pending_lights[LightTrackrec] = true;
} else {
pending_lights[LightTrackrec] = false;
}
if (current_route->muted()) {
pending_lights[LightTrackmute] = true;
} else {
pending_lights[LightTrackmute] = false;
}
if (current_route->soloed()) {
pending_lights[LightTracksolo] = true;
} else {
pending_lights[LightTracksolo] = false;
}
} else {
pending_lights[LightTrackrec] = false;
pending_lights[LightTracksolo] = false;
pending_lights[LightTrackmute] = false;
}
/* global */
if (session.get_auto_loop()) {
pending_lights[LightLoop] = true;
} else {
pending_lights[LightLoop] = false;
}
if (session.get_punch_in() || session.get_punch_out()) {
pending_lights[LightPunch] = true;
} else {
pending_lights[LightPunch] = false;
}
if (session.get_record_enabled()) {
pending_lights[LightRecord] = true;
} else {
pending_lights[LightRecord] = false;
}
if (session.soloing ()) {
pending_lights[LightAnysolo] = true;
} else {
pending_lights[LightAnysolo] = false;
}
/* flush changed light change */
if (pending_lights[LightRecord] != lights[LightRecord]) {
if (pending_lights[LightRecord]) {
light_on (LightRecord);
} else {
light_off (LightRecord);
}
}
if (pending_lights[LightTracksolo] != lights[LightTracksolo]) {
if (pending_lights[LightTracksolo]) {
light_on (LightTracksolo);
} else {
light_off (LightTracksolo);
}
}
if (pending_lights[LightTrackmute] != lights[LightTrackmute]) {
if (pending_lights[LightTrackmute]) {
light_on (LightTrackmute);
} else {
light_off (LightTrackmute);
}
}
if (pending_lights[LightTracksolo] != lights[LightTracksolo]) {
if (pending_lights[LightTracksolo]) {
light_on (LightTracksolo);
} else {
light_off (LightTracksolo);
}
}
if (pending_lights[LightAnysolo] != lights[LightAnysolo]) {
if (pending_lights[LightAnysolo]) {
light_on (LightAnysolo);
} else {
light_off (LightAnysolo);
}
}
if (pending_lights[LightLoop] != lights[LightLoop]) {
if (pending_lights[LightLoop]) {
light_on (LightLoop);
} else {
light_off (LightLoop);
}
}
if (pending_lights[LightPunch] != lights[LightPunch]) {
if (pending_lights[LightPunch]) {
light_on (LightPunch);
} else {
light_off (LightPunch);
}
}
return 0;
}
int
TranzportControlProtocol::process (uint8_t* buf)
{
// printf("read: %02x %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
uint32_t this_button_mask;
@ -672,86 +928,13 @@ TranzportControlProtocol::read (uint32_t timeout_override)
void
TranzportControlProtocol::show_current_track ()
{
for (vector<sigc::connection>::iterator i = track_connections.begin(); i != track_connections.end(); ++i) {
(*i).disconnect ();
}
track_connections.clear ();
if (current_route == 0) {
print (0, 0, "--------");
return;
}
string name = current_route->name();
print (0, 0, name.substr (0, 8).c_str());
track_solo_changed (0);
track_mute_changed (0);
track_rec_changed (0);
track_connections.push_back (current_route->solo_changed.connect (mem_fun (*this, &TranzportControlProtocol::track_solo_changed)));
track_connections.push_back (current_route->mute_changed.connect (mem_fun (*this, &TranzportControlProtocol::track_mute_changed)));
track_connections.push_back (current_route->record_enable_changed.connect (mem_fun (*this, &TranzportControlProtocol::track_rec_changed)));
track_connections.push_back (current_route->gain_changed.connect (mem_fun (*this, &TranzportControlProtocol::track_gain_changed)));
}
void
TranzportControlProtocol::record_status_changed ()
{
if (session.get_record_enabled()) {
light_on (LightRecord);
} else {
light_off (LightRecord);
print (0, 0, current_route->name().substr (0, 8).c_str());
}
}
void
TranzportControlProtocol::track_gain_changed (void* ignored)
{
char buf[8];
switch (display_mode) {
case DisplayNormal:
snprintf (buf, sizeof (buf), "%.1fdB", coefficient_to_dB (current_route->gain()));
print (0, 9, buf);
break;
default:
break;
}
}
void
TranzportControlProtocol::track_solo_changed (void* ignored)
{
if (current_route->soloed()) {
light_on (LightTracksolo);
} else {
light_off (LightTracksolo);
}
}
void
TranzportControlProtocol::track_mute_changed (void *ignored)
{
if (current_route->muted()) {
light_on (LightTrackmute);
} else {
light_off (LightTrackmute);
}
}
void
TranzportControlProtocol::track_rec_changed (void *ignored)
{
if (current_route->record_enabled()) {
light_on (LightTrackrec);
} else {
light_off (LightTrackrec);
}
}
void
TranzportControlProtocol::button_event_battery_press (bool shifted)
{
@ -798,11 +981,7 @@ void
TranzportControlProtocol::button_event_trackrec_press (bool shifted)
{
if (shifted) {
if (session.get_record_enabled()) {
session.record_disenable_all ();
} else {
session.record_enable_all ();
}
toggle_all_rec_enables ();
} else {
if (current_route) {
AudioTrack* at = dynamic_cast<AudioTrack*>(current_route);
@ -855,9 +1034,9 @@ void
TranzportControlProtocol::button_event_undo_press (bool shifted)
{
if (shifted) {
session.redo (1);
redo ();
} else {
session.undo (1);
undo ();
}
}
@ -907,6 +1086,8 @@ TranzportControlProtocol::button_event_loop_press (bool shifted)
{
if (shifted) {
next_wheel_shift_mode ();
} else {
loop_toggle ();
}
}
@ -933,8 +1114,7 @@ TranzportControlProtocol::button_event_prev_release (bool shifted)
void
TranzportControlProtocol::button_event_add_press (bool shifted)
{
jack_nframes_t when = session.audible_frame();
session.locations()->add (new Location (when, when, _("unnamed"), Location::IsMark));
add_marker ();
}
void
@ -961,9 +1141,9 @@ void
TranzportControlProtocol::button_event_rewind_press (bool shifted)
{
if (shifted) {
session.goto_start ();
goto_start ();
} else {
session.request_transport_speed (-2.0f);
rewind ();
}
}
@ -976,9 +1156,10 @@ void
TranzportControlProtocol::button_event_fastforward_press (bool shifted)
{
if (shifted) {
session.goto_end();
goto_end ();
} else {
session.request_transport_speed (2.0f);}
ffwd ();
}
}
void
@ -992,7 +1173,7 @@ TranzportControlProtocol::button_event_stop_press (bool shifted)
if (shifted) {
next_display_mode ();
} else {
session.request_transport_speed (0.0);
transport_stop ();
}
}
@ -1004,7 +1185,7 @@ TranzportControlProtocol::button_event_stop_release (bool shifted)
void
TranzportControlProtocol::button_event_play_press (bool shifted)
{
session.request_transport_speed (1.0);
transport_play ();
}
void
@ -1016,22 +1197,9 @@ void
TranzportControlProtocol::button_event_record_press (bool shifted)
{
if (shifted) {
session.save_state ("");
save_state ();
} else {
switch (session.record_status()) {
case Session::Disabled:
if (session.ntracks() == 0) {
// string txt = _("Please create 1 or more track\nbefore trying to record.\nCheck the Session menu.");
// MessageDialog msg (*editor, txt);
// msg.run ();
return;
}
session.maybe_enable_record ();
break;
case Session::Recording:
case Session::Enabled:
session.disable_record (true);
}
rec_enable_toggle ();
}
}
@ -1139,7 +1307,7 @@ TranzportControlProtocol::scrub ()
if (dir != last_wheel_dir) {
/* changed direction, start over */
speed = 1.0f;
speed = 0.1f;
} else {
if (timerisset (&last_wheel_motion)) {
@ -1160,7 +1328,7 @@ TranzportControlProtocol::scrub ()
last_wheel_motion = now;
last_wheel_dir = dir;
session.request_transport_speed (speed * dir);
move_at (speed * dir);
}
void
@ -1245,34 +1413,12 @@ TranzportControlProtocol::next_wheel_mode ()
show_wheel_mode ();
}
void
TranzportControlProtocol::next_marker ()
{
Location *location = session.locations()->first_location_after (session.transport_frame());
if (location) {
session.request_locate (location->start(), session.transport_rolling());
} else {
session.request_locate (session.current_end_frame());
}
}
void
TranzportControlProtocol::prev_marker ()
{
Location *location = session.locations()->first_location_before (session.transport_frame());
if (location) {
session.request_locate (location->start(), session.transport_rolling());
} else {
session.goto_start ();
}
}
void
TranzportControlProtocol::next_track ()
{
uint32_t limit = session.nroutes();
uint32_t start = current_track_id;
Route* cr = current_route;
if (current_track_id == limit) {
current_track_id = 0;
@ -1281,7 +1427,7 @@ TranzportControlProtocol::next_track ()
}
while (current_track_id < limit) {
if ((current_route = session.route_by_remote_id (current_track_id)) != 0) {
if ((cr = session.route_by_remote_id (current_track_id)) != 0) {
break;
}
current_track_id++;
@ -1289,14 +1435,24 @@ TranzportControlProtocol::next_track ()
if (current_track_id == limit) {
current_track_id = 0;
while (current_track_id != start) {
if ((cr = session.route_by_remote_id (current_track_id)) != 0) {
break;
}
current_track_id++;
}
}
show_current_track ();
current_route = cr;
}
void
TranzportControlProtocol::prev_track ()
{
uint32_t limit = session.nroutes() - 1;
uint32_t start = current_track_id;
Route* cr = current_route;
if (current_track_id == 0) {
current_track_id = session.nroutes() - 1;
} else {
@ -1304,17 +1460,37 @@ TranzportControlProtocol::prev_track ()
}
while (current_track_id >= 0) {
if ((current_route = session.route_by_remote_id (current_track_id)) != 0) {
if ((cr = session.route_by_remote_id (current_track_id)) != 0) {
break;
}
current_track_id--;
}
if (current_track_id < 0) {
current_track_id = 0;
current_track_id = limit;
while (current_track_id > start) {
if ((cr = session.route_by_remote_id (current_track_id)) != 0) {
break;
}
current_track_id--;
}
}
show_current_track ();
current_route = cr;
}
void
TranzportControlProtocol::set_current_track (Route* r)
{
TranzportRequest* req = get_request (SetCurrentTrack);
if (req == 0) {
return;
}
req->track = r;
send_request (req);
}
void
@ -1390,13 +1566,9 @@ TranzportControlProtocol::print (int row, int col, const char *text)
int offset = col % 4;
{
LockMonitor lm (print_lock, __LINE__, __FILE__);
/* copy current cell contents into tmp */
memcpy (tmp, &current_screen[row][base_col], 4);
memcpy (tmp, &pending_screen[row][base_col], 4);
/* overwrite with new text */
@ -1404,32 +1576,28 @@ TranzportControlProtocol::print (int row, int col, const char *text)
memcpy (tmp+offset, text, tocopy);
uint8_t cmd[8];
/* copy it back to pending */
/* compare with current screen */
if (memcmp (tmp, &current_screen[row][base_col], 4)) {
/* different, so update */
memcpy (&current_screen[row][base_col], tmp, 4);
cmd[0] = 0x00;
cmd[1] = 0x01;
cmd[2] = cell + (row * 5);
cmd[3] = tmp[0];
cmd[4] = tmp[1];
cmd[5] = tmp[2];
cmd[6] = tmp[3];
cmd[7] = 0x00;
write (cmd, 500);
}
memcpy (&pending_screen[row][base_col], tmp, 4);
text += tocopy;
left -= tocopy;
col += tocopy;
}
}
}
bool
TranzportControlProtocol::caller_is_ui_thread ()
{
return (pthread_self() == thread);
}
void
TranzportControlProtocol::do_request (TranzportRequest* req)
{
if (req->type == SetCurrentTrack) {
current_route = req->track;
}
return;
}

View file

@ -10,18 +10,29 @@
#include <ardour/control_protocol.h>
#include <ardour/types.h>
namespace ARDOUR {
#include <pbd/abstract_ui.h>
class TranzportControlProtocol : public ControlProtocol {
extern BaseUI::RequestType LEDChange;
extern BaseUI::RequestType Print;
extern BaseUI::RequestType SetCurrentTrack;
struct TranzportRequest : public BaseUI::BaseRequestObject {
int led;
int row;
int col;
char* text;
ARDOUR::Route* track;
};
class TranzportControlProtocol : public ARDOUR::ControlProtocol, public AbstractUI<TranzportRequest>
{
public:
TranzportControlProtocol (Session&);
TranzportControlProtocol (ARDOUR::Session&);
virtual ~TranzportControlProtocol();
int init ();
bool active() const;
int set_active (bool yn);
void send_route_feedback (std::list<Route*>&);
void send_global_feedback ();
bool caller_is_ui_thread();
private:
static const int VENDORID = 0x165b;
@ -89,19 +100,19 @@ class TranzportControlProtocol : public ControlProtocol {
uint8_t _datawheel;
uint8_t _device_status;
usb_dev_handle* udev;
Route* current_route;
ARDOUR::Route* current_route;
uint32_t current_track_id;
char current_screen[2][20];
bool lights[7];
WheelMode wheel_mode;
WheelShiftMode wheel_shift_mode;
struct timeval last_wheel_motion;
int last_wheel_dir;
DisplayMode display_mode;
DisplayMode requested_display_mode;
uint32_t last_meter_fill;
std::vector<sigc::connection> track_connections;
void do_request (TranzportRequest*);
PBD::Lock update_lock;
char current_screen[2][20];
char pending_screen[2][20];
bool lights[7];
bool pending_lights[7];
bool last_negative;
uint32_t last_hrs;
@ -109,9 +120,12 @@ class TranzportControlProtocol : public ControlProtocol {
uint32_t last_secs;
uint32_t last_frames;
jack_nframes_t last_where;
ARDOUR::gain_t last_track_gain;
uint32_t last_meter_fill;
struct timeval last_wheel_motion;
int last_wheel_dir;
PBD::Lock write_lock;
PBD::Lock print_lock;
PBD::Lock io_lock;
int open ();
int read (uint32_t timeout_override = 0);
@ -128,21 +142,19 @@ class TranzportControlProtocol : public ControlProtocol {
void enter_big_meter_mode ();
void enter_normal_display_mode ();
void next_display_mode ();
void normal_update ();
void show_current_track ();
void show_track_gain ();
void show_transport_time ();
void show_wheel_mode ();
void show_gain ();
void show_pan ();
void show_meter ();
void track_solo_changed (void*);
void track_rec_changed (void*);
void track_mute_changed (void*);
void track_gain_changed (void*);
void record_status_changed ();
void datawheel ();
void scrub ();
void scroll ();
@ -151,10 +163,9 @@ class TranzportControlProtocol : public ControlProtocol {
void next_wheel_mode ();
void next_wheel_shift_mode ();
void set_current_track (ARDOUR::Route*);
void next_track ();
void prev_track ();
void next_marker ();
void prev_marker ();
void step_gain_up ();
void step_gain_down ();
void step_pan_right ();
@ -203,8 +214,10 @@ class TranzportControlProtocol : public ControlProtocol {
void button_event_play_release (bool shifted);
void button_event_record_press (bool shifted);
void button_event_record_release (bool shifted);
int process (uint8_t *);
int update_state();
};
} // namespace
#endif // ardour_tranzport_control_protocol_h