a) moved metering and meter falloff code into libardour

b) added initial "big meter" mode for tranzport
c) fixed some lock issues in ARDOUR::IO objects
d) generic_midi control surface module now compiles and loads


git-svn-id: svn://localhost/trunk/ardour2@450 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2006-04-10 03:54:00 +00:00
parent 7e8a982245
commit a157537898
36 changed files with 530 additions and 380 deletions

View File

@ -211,11 +211,29 @@
<menuitem action='AutoConnectNewTrackOutputsToHardware'/>
<menuitem action='ManuallyConnectNewTrackOutputs'/>
</menu>
<menu action='ControlSurfaces'/>
<menu action='Monitoring'>
<menuitem action='UseHardwareMonitoring'/>
<menuitem action='UseSoftwareMonitoring'/>
<menuitem action='UseExternalMonitoring'/>
</menu>
<menu action='Metering'>
<menu action='MeteringFallOffRate'>
<menuitem action='MeterFalloffOff'/>
<menuitem action='MeterFalloffSlowest'/>
<menuitem action='MeterFalloffSlow'/>
<menuitem action='MeterFalloffMedium'/>
<menuitem action='MeterFalloffFast'/>
<menuitem action='MeterFalloffFaster'/>
<menuitem action='MeterFalloffFastest'/>
</menu>
<menu action='MeteringHoldTime'>
<menuitem action='MeterHoldOff'/>
<menuitem action='MeterHoldShort'/>
<menuitem action='MeterHoldMedium'/>
<menuitem action='MeterHoldLong'/>
</menu>
</menu>
<menu action='Solo'>
<menuitem action='LatchedSolo'/>
<menuitem action='SoloInPlace'/>
@ -250,6 +268,7 @@
<menuitem action='StopRecordingOnXrun'/>
<menuitem action='StopTransportAtEndOfSession'/>
<menuitem action='GainReduceFastTransport'/>
<separator/>
</menu>
<menu name='Help' action='Help'>
<menu name='KeyMouse Actions' action='KeyMouse Actions'>

View File

@ -497,27 +497,12 @@ ARDOUR_UI::every_second ()
update_cpu_load ();
update_buffer_load ();
update_disk_space ();
// update_disk_rate ();
return TRUE;
}
gint
ARDOUR_UI::every_point_one_seconds ()
{
struct timeval now;
struct timeval diff;
/* do not attempt to grab peak power more than once per cycle.
*/
gettimeofday (&now, 0);
timersub (&now, &last_peak_grab, &diff);
if ((diff.tv_usec + (diff.tv_sec * 1000000)) >= engine->usecs_per_cycle()) {
IO::GrabPeakPower(); /* EMIT_SIGNAL */
last_peak_grab = now;
}
update_speed_display ();
RapidScreenUpdate(); /* EMIT_SIGNAL */
return TRUE;
@ -567,20 +552,6 @@ ARDOUR_UI::update_cpu_load ()
cpu_load_label.set_text (buf);
}
void
ARDOUR_UI::update_disk_rate ()
{
char buf[64];
if (session) {
snprintf (buf, sizeof (buf), _("Disk r:%5.1f w:%5.1f MB/s"),
session->read_data_rate()/1048576.0f, session->write_data_rate()/1048576.0f);
disk_rate_label.set_text (buf);
} else {
disk_rate_label.set_text ("");
}
}
void
ARDOUR_UI::update_buffer_load ()
{

View File

@ -87,6 +87,7 @@ namespace ARDOUR {
class Route;
class Port;
class IO;
class ControlProtocolInfo;
};
namespace ALSA {
@ -491,6 +492,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI
Gtk::HBox menu_hbox;
void build_menu_bar ();
void build_control_surface_menu ();
void pack_toplevel_controls();
Gtk::Label wall_clock_label;
@ -505,10 +507,6 @@ class ARDOUR_UI : public Gtkmm2ext::UI
Gtk::EventBox cpu_load_box;
void update_cpu_load ();
Gtk::Label disk_rate_label;
Gtk::EventBox disk_rate_box;
void update_disk_rate();
Gtk::Label buffer_load_label;
Gtk::EventBox buffer_load_box;
void update_buffer_load ();
@ -718,7 +716,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI
void map_some_session_state (const char* group, const char* action, bool (ARDOUR::Session::*get)() const);
void queue_session_control_changed (ARDOUR::Session::ControlType t);
void session_control_changed (ARDOUR::Session::ControlType t);
void toggle_control_protocol (ARDOUR::ControlProtocolInfo*);
};

View File

@ -193,7 +193,6 @@ ARDOUR_UI::unload_session ()
session = 0;
update_buffer_load ();
// update_disk_rate ();
return 0;
}

View File

@ -33,6 +33,7 @@
#include "actions.h"
#include <ardour/session.h>
#include <ardour/control_protocol_manager.h>
#include "i18n.h"
@ -76,6 +77,7 @@ ARDOUR_UI::install_actions ()
ActionManager::register_action (main_actions, X_("AudioFileFormat"), _("Audio File Format"));
ActionManager::register_action (main_actions, X_("AudioFileFormatHeader"), _("Header"));
ActionManager::register_action (main_actions, X_("AudioFileFormatData"), _("Data"));
ActionManager::register_action (main_actions, X_("ControlSurfaces"), _("Control Surfaces"));
/* the real actions */
@ -441,9 +443,51 @@ ARDOUR_UI::install_actions ()
setup_config_options ();
}
void
ARDOUR_UI::toggle_control_protocol (ControlProtocolInfo* cpi)
{
if (cpi->protocol == 0) {
ControlProtocolManager::instance().instantiate (*cpi);
} else {
ControlProtocolManager::instance().teardown (*cpi);
}
}
void
ARDOUR_UI::build_control_surface_menu ()
{
list<ControlProtocolInfo*>::iterator i;
/* !!! this has to match the top level entry from ardour.menus */
string ui = "<menubar name='Main' action='MainMenu'>\n<menu name='Options' action='Options'>\n<menu action='ControlSurfaces'><separator/>\n";
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 += "Surface";
string action_label = (*i)->name;
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)));
ui += "<menuitem action='";
ui += action_name;
ui += "'/>\n";
}
ui += "</menu>\n</menu>\n</menubar>\n";
ActionManager::ui_manager->add_ui_from_string (ui);
}
void
ARDOUR_UI::build_menu_bar ()
{
build_control_surface_menu ();
menu_bar = dynamic_cast<MenuBar*> (ActionManager::get_widget (X_("/Main")));
menu_bar->set_name ("MainMenuBar");
@ -470,10 +514,6 @@ ARDOUR_UI::build_menu_bar ()
buffer_load_box.set_name ("BufferLoad");
buffer_load_label.set_name ("BufferLoad");
// disk_rate_box.add (disk_rate_label);
// disk_rate_box.set_name ("DiskRate");
// disk_rate_label.set_name ("DiskRate");
sample_rate_box.add (sample_rate_label);
sample_rate_box.set_name ("SampleRate");
sample_rate_label.set_name ("SampleRate");
@ -482,7 +522,6 @@ ARDOUR_UI::build_menu_bar ()
menu_hbox.pack_end (wall_clock_box, false, false, 10);
menu_hbox.pack_end (disk_space_box, false, false, 10);
menu_hbox.pack_end (cpu_load_box, false, false, 10);
// menu_hbox.pack_end (disk_rate_box, false, false, 10);
menu_hbox.pack_end (buffer_load_box, false, false, 10);
menu_hbox.pack_end (sample_rate_box, false, false, 10);

View File

@ -966,7 +966,7 @@ Editor::control_scroll (float fraction)
} else if ((fraction > 0.0f) && (max_frames - session->transport_frame() < step)) {
target = (max_frames - (current_page_frames()*2)); // allow room for slop in where the PH is on the screen
} else {
target = (session->transport_frame() + (jack_nframes_t)(fraction * current_page_frames()));
target = (session->transport_frame() + (jack_nframes_t) floor ((fraction * current_page_frames())));
}
/* move visuals, we'll catch up with it later */
@ -1097,9 +1097,6 @@ Editor::start_scrolling ()
{
scroll_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect
(mem_fun(*this, &Editor::update_current_screen));
slower_update_connection = ARDOUR_UI::instance()->RapidScreenUpdate.connect
(mem_fun(*this, &Editor::update_slower));
}
void

View File

@ -18,7 +18,8 @@ void
Editor::register_actions ()
{
RefPtr<Action> act;
RefPtr<ActionGroup> editor_actions = ActionGroup::create (X_("Editor"));
editor_actions = ActionGroup::create (X_("Editor"));
/* non-operative menu items for menu bar */
@ -37,6 +38,9 @@ Editor::register_actions ()
ActionManager::register_action (editor_actions, X_("Monitoring"), _("Monitoring"));
ActionManager::register_action (editor_actions, X_("Autoconnect"), _("Autoconnect"));
ActionManager::register_action (editor_actions, X_("Layering"), _("Layering"));
ActionManager::register_action (editor_actions, X_("Metering"), _("Metering"));
ActionManager::register_action (editor_actions, X_("MeteringFallOffRate"), _("Fall off rate"));
ActionManager::register_action (editor_actions, X_("MeteringHoldTime"), _("Hold Time"));
ActionManager::register_action (editor_actions, X_("addExistingAudioFiles"), _("Add Existing Audio"));
/* add named actions for the editor */

View File

@ -204,14 +204,6 @@ Editor::update_current_screen ()
}
}
void
Editor::update_slower ()
{
if (current_mixer_strip) {
current_mixer_strip->update ();
}
}
void
Editor::current_mixer_strip_removed ()
{

View File

@ -318,10 +318,8 @@ GainMeter::update_meters ()
if ((*i).packed) {
peak = _io.peak_input_power (n);
if (_session.meter_falloff() == 0.0f || peak > (*i).meter->get_user_level()) {
(*i).meter->set (log_meter (peak), peak);
}
(*i).meter->set (log_meter (peak), peak);
if (peak > max_peak) {
max_peak = peak;
/* set peak display */
@ -334,32 +332,8 @@ GainMeter::update_meters ()
}
}
}
}
void
GainMeter::update_meters_falloff ()
{
vector<MeterInfo>::iterator i;
uint32_t n;
float dbpeak;
for (n = 0, i = meters.begin(); i != meters.end(); ++i, ++n) {
if ((*i).packed) {
// just do falloff
//peak = (*i).meter->get_level() * _falloff_rate;
dbpeak = (*i).meter->get_user_level() - _session.meter_falloff();
dbpeak = std::max(dbpeak, -200.0f);
// cerr << "tmplevel: " << tmplevel << endl;
(*i).meter->set (log_meter (dbpeak), dbpeak);
}
}
}
void
GainMeter::meter_hold_changed()
{

View File

@ -766,18 +766,10 @@ MixerStrip::update_output_display ()
panners.setup_pan ();
}
void
MixerStrip::update ()
{
gpm.update_meters ();
}
void
MixerStrip::fast_update ()
{
if (_session.meter_falloff() > 0.0f) {
gpm.update_meters_falloff ();
}
gpm.update_meters ();
}
gint

View File

@ -89,7 +89,6 @@ class MixerStrip : public RouteUI, public Gtk::EventBox
void set_width (Width);
Width get_width() const { return _width; }
void update ();
void fast_update ();
void set_embedded (bool);

View File

@ -398,7 +398,6 @@ Mixer_UI::hide_strip (MixerStrip* ms)
gint
Mixer_UI::start_updating ()
{
screen_update_connection = ARDOUR_UI::instance()->RapidScreenUpdate.connect (mem_fun(*this, &Mixer_UI::update_strips));
fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun(*this, &Mixer_UI::fast_update_strips));
return 0;
}
@ -406,21 +405,10 @@ Mixer_UI::hide_strip (MixerStrip* ms)
gint
Mixer_UI::stop_updating ()
{
screen_update_connection.disconnect();
fast_screen_update_connection.disconnect();
return 0;
}
void
Mixer_UI::update_strips ()
{
if (is_mapped () && session) {
for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
(*i)->update ();
}
}
}
void
Mixer_UI::fast_update_strips ()
{

View File

@ -131,8 +131,6 @@ class Mixer_UI : public Gtk::Window
void disconnect_from_session ();
sigc::connection screen_update_connection;
void update_strips ();
sigc::connection fast_screen_update_connection;
void fast_update_strips ();

View File

@ -7,6 +7,7 @@
#include <glib.h>
#include <gdk/gdktypes.h>
#include <gtkmm/window.h>
#include <gtkmm/actiongroup.h>
#include <jack/types.h>
#include <sigc++/signal.h>
@ -129,11 +130,10 @@ class PublicEditor : public Gtk::Window, public Stateful {
sigc::signal<void,Editing::DisplayControl> DisplayControlChanged;
sigc::signal<void> ZoomFocusChanged;
sigc::signal<void> ZoomChanged;
sigc::signal<void> XOriginChanged;
sigc::signal<void> Resized;
sigc::signal<void> Realized;
// FIXED FOR GTK2
Glib::RefPtr<Gtk::ActionGroup> editor_actions;
virtual bool canvas_control_point_event (GdkEvent* event,ArdourCanvas::Item*, ControlPoint*) = 0;
virtual bool canvas_line_event (GdkEvent* event,ArdourCanvas::Item*, AutomationLine*) = 0;

View File

@ -63,7 +63,7 @@ SendUI::SendUI (Send& s, Session& se)
gpm.setup_meters ();
gpm.set_fader_name ("SendUIFrame");
screen_update_connection = ARDOUR_UI::instance()->RapidScreenUpdate.connect (mem_fun (*this, &SendUI::update));
// screen_update_connection = ARDOUR_UI::instance()->RapidScreenUpdate.connect (mem_fun (*this, &SendUI::update));
fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun (*this, &SendUI::fast_update));
}
@ -107,14 +107,13 @@ SendUI::send_going_away (Redirect *ignored)
void
SendUI::update ()
{
gpm.update_meters ();
}
void
SendUI::fast_update ()
{
if (_session.meter_falloff() > 0.0f) {
gpm.update_meters_falloff ();
gpm.update_meters ();
}
}

View File

@ -108,7 +108,7 @@ TimeAxisView::TimeAxisView (ARDOUR::Session& sess, PublicEditor& ed, TimeAxisVie
name_entry.signal_activate().connect (mem_fun(*this, &TimeAxisView::name_entry_activated));
name_entry.signal_focus_in_event().connect (mem_fun (*this, &TimeAxisView::name_entry_focus_in));
name_entry.signal_focus_out_event().connect (mem_fun (*this, &TimeAxisView::name_entry_focus_out));
Gtkmm2ext::set_size_request_to_display_given_text (name_entry, N_("gTortnam"), 10, 10); // just represents a short name
Gtkmm2ext::set_size_request_to_display_given_text (name_entry, N_("gTortnam"), 2, 2); // just represents a short name
name_label.set_name ("TrackLabel");
name_label.set_alignment (0.0, 0.5);

View File

@ -202,6 +202,8 @@ class AudioEngine : public sigc::trackable
sigc::slot<int,jack_nframes_t> freewheel_action;
bool reconnect_on_halt;
int _usecs_per_cycle;
jack_nframes_t last_meter_point;
jack_nframes_t meter_interval;
typedef std::set<Port*> Ports;
Ports ports;
@ -232,9 +234,13 @@ class AudioEngine : public sigc::trackable
int jack_sample_rate_callback (jack_nframes_t);
static void halted (void *);
static void meter (Port *port, jack_nframes_t nframes);
int connect_to_jack (std::string client_name);
static void* _meter_thread (void* arg);
void* meter_thread ();
pthread_t meter_thread_id;
void maybe_start_metering_thread ();
};
}; /* namespace ARDOUR */

View File

@ -30,7 +30,6 @@ class ControlProtocol : public sigc::trackable {
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; }
@ -77,9 +76,10 @@ class ControlProtocol : public sigc::trackable {
extern "C" {
struct ControlProtocolDescriptor {
const char* name;
void* ptr;
void* module;
const char* name; /* descriptive */
const char* id; /* unique and version-specific */
void* ptr; /* protocol can store a value here */
void* module; /* not for public access */
ControlProtocol* (*initialize)(ControlProtocolDescriptor*,Session*);
void (*destroy)(ControlProtocolDescriptor*,ControlProtocol*);

View File

@ -4,12 +4,15 @@
#include <string>
#include <list>
#include <sigc++/sigc++.h>
#include <pbd/lockmonitor.h>
namespace ARDOUR {
class ControlProtocol;
class ControlProtocolDescriptor;
class Session;
struct ControlProtocolInfo {
ControlProtocolDescriptor* descriptor;
@ -18,7 +21,7 @@ struct ControlProtocolInfo {
std::string path;
};
class ControlProtocolManager
class ControlProtocolManager : public sigc::trackable
{
public:
ControlProtocolManager ();
@ -26,19 +29,24 @@ class ControlProtocolManager
static ControlProtocolManager& instance() { return *_instance; }
void set_session (Session&);
void discover_control_protocols (std::string search_path);
void startup (Session&);
void foreach_known_protocol (sigc::slot<void,const ControlProtocolInfo*>);
ControlProtocol* instantiate (Session&, std::string protocol_name);
int teardown (std::string protocol_name);
ControlProtocol* instantiate (ControlProtocolInfo&);
int teardown (ControlProtocolInfo&);
std::list<ControlProtocolInfo*> control_protocol_info;
private:
static ControlProtocolManager* _instance;
Session* _session;
PBD::Lock protocols_lock;
std::list<ControlProtocolInfo*> control_protocol_info;
std::list<ControlProtocol*> control_protocols;
void drop_session ();
int control_protocol_discover (std::string path);
ControlProtocolDescriptor* get_descriptor (std::string path);
};

View File

@ -198,19 +198,14 @@ class IO : public Stateful, public ARDOUR::StateManager
/* Peak metering */
float peak_input_power (uint32_t n) {
if (n < std::max(_ninputs, _noutputs)) {
float x = _stored_peak_power[n];
if(x > 0.0) {
return 20 * fast_log10(x);
} else {
return minus_infinity();
}
if (n < std::max (_ninputs, _noutputs)) {
return _visible_peak_power[n];
} else {
return minus_infinity();
}
}
static sigc::signal<void> GrabPeakPower;
static sigc::signal<void> Meter;
/* automation */
@ -278,7 +273,7 @@ class IO : public Stateful, public ARDOUR::StateManager
vector<Port*> _outputs;
vector<Port*> _inputs;
vector<float> _peak_power;
vector<float> _stored_peak_power;
vector<float> _visible_peak_power;
string _name;
Connection* _input_connection;
Connection* _output_connection;
@ -394,7 +389,7 @@ class IO : public Stateful, public ARDOUR::StateManager
int make_connections (const XMLNode&);
void setup_peak_meters ();
void grab_peak_power ();
void meter ();
bool ensure_inputs_locked (uint32_t, bool clear, void *src);
bool ensure_outputs_locked (uint32_t, bool clear, void *src);

View File

@ -21,6 +21,7 @@
#include <unistd.h>
#include <cerrno>
#include <vector>
#include <pbd/pthread_utils.h>
#include <ardour/audioengine.h>
@ -59,6 +60,9 @@ AudioEngine::AudioEngine (string client_name)
_buffer_size = 0;
_freewheeling = false;
_freewheel_thread_registered = false;
last_meter_point = 0;
meter_interval = 0;
meter_thread_id = (pthread_t) 0;
if (connect_to_jack (client_name)) {
throw NoBackendAvailable ();
@ -71,6 +75,10 @@ AudioEngine::~AudioEngine ()
if (_running) {
jack_client_close (_jack);
}
if (meter_thread_id != (pthread_t) 0) {
pthread_cancel (meter_thread_id);
}
}
void
@ -216,59 +224,6 @@ AudioEngine::_freewheel_callback (int onoff, void *arg)
static_cast<AudioEngine*>(arg)->_freewheeling = onoff;
}
void
AudioEngine::meter (Port *port, jack_nframes_t nframes)
{
double peak;
uint32_t overlen;
jack_default_audio_sample_t *buf;
buf = port->get_buffer (nframes);
peak = port->_peak;
overlen = port->overlen;
{
for (jack_nframes_t n = 0; n < nframes; ++n) {
/* 1) peak metering */
peak = f_max (peak, buf[n]);
/* 2) clip/over metering */
if (buf[n] >= 1.0) {
overlen++;
} else if (overlen) {
if (overlen > Port::short_over_length) {
port->_short_overs++;
}
if (overlen > Port::long_over_length) {
port->_long_overs++;
}
overlen = 0;
}
}
}
/* post-loop check on the final status of overlen */
if (overlen > Port::short_over_length) {
port->_short_overs++;
}
if (overlen > Port::long_over_length) {
port->_short_overs++;
}
if (peak > 0.0) {
port->_peak_db= 20 * fast_log10 (peak);
} else {
port->_peak_db = minus_infinity();
}
port->_peak = peak;
port->overlen = overlen;
}
int
AudioEngine::process_callback (jack_nframes_t nframes)
{
@ -304,14 +259,6 @@ AudioEngine::process_callback (jack_nframes_t nframes)
return 0;
}
/* do input peak metering */
for (Ports::iterator i = ports.begin(); i != ports.end(); ++i) {
if ((*i)->metering) {
meter ((*i), nframes);
}
}
session->process (nframes);
if (!_running) {
@ -323,6 +270,13 @@ AudioEngine::process_callback (jack_nframes_t nframes)
return 0;
}
/* manage meters */
if ((meter_interval > _buffer_size) && (last_meter_point + meter_interval < next_processed_frames)) {
IO::Meter ();
last_meter_point = next_processed_frames;
}
if (last_monitor_check + monitor_check_interval < next_processed_frames) {
for (Ports::iterator i = ports.begin(); i != ports.end(); ++i) {
@ -360,6 +314,11 @@ AudioEngine::jack_sample_rate_callback (jack_nframes_t nframes)
monitor_check_interval = nframes / 10;
last_monitor_check = 0;
meter_interval = nframes / 100;
last_meter_point = 0;
maybe_start_metering_thread ();
if (session) {
session->set_frame_rate (nframes);
@ -391,6 +350,47 @@ AudioEngine::jack_bufsize_callback (jack_nframes_t nframes)
session->set_block_size (_buffer_size);
}
maybe_start_metering_thread ();
return 0;
}
void
AudioEngine::maybe_start_metering_thread ()
{
if (meter_interval == 0) {
return;
}
if (_buffer_size == 0) {
return;
}
if (meter_interval < _buffer_size) {
if (meter_thread_id != (pthread_t) 0) {
pthread_cancel (meter_thread_id);
}
pthread_create (&meter_thread_id, 0, _meter_thread, this);
}
}
void*
AudioEngine::_meter_thread (void *arg)
{
return static_cast<AudioEngine*>(arg)->meter_thread ();
}
void*
AudioEngine::meter_thread ()
{
PBD::ThreadCreated (pthread_self(), "Metering");
while (true) {
usleep (10000); /* 1/100th sec interval */
pthread_testcancel();
IO::Meter ();
}
return 0;
}

View File

@ -240,11 +240,10 @@ ControlProtocol::thread_work ()
if (send()) {
// list<Route*> routes = session.get_routes(); /* copies the routes */
// if (send_route_feedback ()) {
//send_route_feedback (routes);
// }
if (send_route_feedback ()) {
list<Route*> routes = session.get_routes(); /* copies the routes */
send_route_feedback (routes);
}
send_global_feedback ();
}

View File

@ -4,6 +4,7 @@
#include <pbd/error.h>
#include <pbd/pathscanner.h>
#include <ardour/session.h>
#include <ardour/control_protocol.h>
#include <ardour/control_protocol_manager.h>
@ -20,6 +21,8 @@ ControlProtocolManager::ControlProtocolManager ()
if (_instance == 0) {
_instance = this;
}
_session = 0;
}
ControlProtocolManager::~ControlProtocolManager()
@ -35,97 +38,75 @@ ControlProtocolManager::~ControlProtocolManager()
}
void
ControlProtocolManager::startup (Session& s)
ControlProtocolManager::set_session (Session& s)
{
list<ControlProtocolInfo *>::iterator i;
for (i = control_protocol_info.begin(); i != control_protocol_info.end(); ++i) {
_session = &s;
_session->going_away.connect (mem_fun (*this, &ControlProtocolManager::drop_session));
}
ControlProtocolInfo* cpi = (*i);
void
ControlProtocolManager::drop_session ()
{
_session = 0;
if (cpi->name == "Tranzport") {
cpi->descriptor = get_descriptor ((*i)->path);
if (cpi->descriptor == 0) {
error << string_compose (_("control protocol name \"%1\" has no descriptor"), cpi->name) << endmsg;
continue;
}
if ((cpi->protocol = cpi->descriptor->initialize (cpi->descriptor, &s)) == 0) {
error << string_compose (_("control protocol name \"%1\" could not be initialized"), cpi->name) << endmsg;
continue;
}
{
LockMonitor lm (protocols_lock, __LINE__, __FILE__);
control_protocols.push_back (cpi->protocol);
}
cpi->protocol->init ();
cpi->protocol->set_active (true);
{
LockMonitor lm (protocols_lock, __LINE__, __FILE__);
for (list<ControlProtocol*>::iterator p = control_protocols.begin(); p != control_protocols.end(); ++p) {
delete *p;
}
control_protocols.clear ();
}
}
ControlProtocol*
ControlProtocolManager::instantiate (Session& session, string name)
ControlProtocolManager::instantiate (ControlProtocolInfo& cpi)
{
list<ControlProtocolInfo *>::iterator i;
for (i = control_protocol_info.begin(); i != control_protocol_info.end(); ++i) {
if ((*i)->name == name) {
break;
}
}
if (i == control_protocol_info.end()) {
error << string_compose (_("control protocol name \"%1\" is unknown"), name) << endmsg;
if (_session == 0) {
return 0;
}
ControlProtocolInfo* cpi = (*i);
cpi.descriptor = get_descriptor (cpi.path);
cpi->descriptor = get_descriptor ((*i)->path);
if (cpi->descriptor == 0) {
error << string_compose (_("control protocol name \"%1\" has no descriptor"), name) << endmsg;
if (cpi.descriptor == 0) {
error << string_compose (_("control protocol name \"%1\" has no descriptor"), cpi.name) << endmsg;
return 0;
}
if ((cpi->protocol = cpi->descriptor->initialize (cpi->descriptor, &session)) == 0) {
error << string_compose (_("control protocol name \"%1\" could not be initialized"), name) << endmsg;
if ((cpi.protocol = cpi.descriptor->initialize (cpi.descriptor, _session)) == 0) {
error << string_compose (_("control protocol name \"%1\" could not be initialized"), cpi.name) << endmsg;
return 0;
}
LockMonitor lm (protocols_lock, __LINE__, __FILE__);
control_protocols.push_back (cpi->protocol);
return cpi->protocol;
control_protocols.push_back (cpi.protocol);
return cpi.protocol;
}
int
ControlProtocolManager::teardown (string name)
ControlProtocolManager::teardown (ControlProtocolInfo& cpi)
{
for (list<ControlProtocolInfo*>::iterator i = control_protocol_info.begin(); i != control_protocol_info.end(); ++i) {
ControlProtocolInfo* cpi = *i;
if (cpi->name == name && cpi->descriptor && cpi->protocol) {
cpi->descriptor->destroy (cpi->descriptor, cpi->protocol);
{
LockMonitor lm (protocols_lock, __LINE__, __FILE__);
list<ControlProtocol*>::iterator p = find (control_protocols.begin(), control_protocols.end(), cpi->protocol);
if (p != control_protocols.end()) {
control_protocols.erase (p);
}
}
cpi->protocol = 0;
return 0;
}
if (!cpi.protocol) {
return 0;
}
return -1;
if (!cpi.descriptor) {
return 0;
}
cpi.descriptor->destroy (cpi.descriptor, cpi.protocol);
{
LockMonitor lm (protocols_lock, __LINE__, __FILE__);
list<ControlProtocol*>::iterator p = find (control_protocols.begin(), control_protocols.end(), cpi.protocol);
if (p != control_protocols.end()) {
control_protocols.erase (p);
}
}
cpi.protocol = 0;
dlclose (cpi.descriptor->module);
return 0;
}
static bool protocol_filter (const string& str, void *arg)
@ -141,12 +122,9 @@ ControlProtocolManager::discover_control_protocols (string path)
vector<string *> *found;
PathScanner scanner;
cerr << "CP Manager looking for surfaces\n";
found = scanner (path, protocol_filter, 0, false, true);
for (vector<string*>::iterator i = found->begin(); i != found->end(); ++i) {
cerr << "CP Manager looking at " << **i << endl;
control_protocol_discover (**i);
delete *i;
}
@ -166,15 +144,12 @@ ControlProtocolManager::control_protocol_discover (string path)
info->descriptor = descriptor;
info->name = descriptor->name;
info->path = path;
control_protocol_info.push_back (info);
info->protocol = 0;
cerr << "Found \"" << info->name << "\"\n";
control_protocol_info.push_back (info);
dlclose (descriptor->module);
} else {
cerr << "no descriptor\n";
}
return 0;
@ -212,3 +187,11 @@ ControlProtocolManager::get_descriptor (string path)
return descriptor;
}
void
ControlProtocolManager::foreach_known_protocol (sigc::slot<void,const ControlProtocolInfo*> method)
{
for (list<ControlProtocolInfo*>::iterator i = control_protocol_info.begin(); i != control_protocol_info.end(); ++i) {
method (*i);
}
}

View File

@ -353,7 +353,7 @@ find_file (string name, string dir, string subdir = "")
path = *i;
path += "/" + name;
if (access (path.c_str(), R_OK) == 0) {
cerr << "Using file " << path << " found in ARDOUR_PATH." << endl;
// cerr << "Using file " << path << " found in ARDOUR_PATH." << endl;
return path;
}
}

View File

@ -63,7 +63,7 @@ const string IO::state_node_name = "IO";
bool IO::connecting_legal = false;
bool IO::ports_legal = false;
bool IO::panners_legal = false;
sigc::signal<void> IO::GrabPeakPower;
sigc::signal<void> IO::Meter;
sigc::signal<int> IO::ConnectingLegal;
sigc::signal<int> IO::PortsLegal;
sigc::signal<int> IO::PannersLegal;
@ -127,7 +127,7 @@ IO::IO (Session& s, string name,
_gain_automation_state = Off;
_gain_automation_style = Absolute;
GrabPeakPower.connect (mem_fun (*this, &IO::grab_peak_power));
Meter.connect (mem_fun (*this, &IO::meter));
}
IO::~IO ()
@ -1171,11 +1171,11 @@ IO::ensure_io (uint32_t nin, uint32_t nout, bool clear, void* src)
_session.engine().disconnect (*i);
}
}
}
if (in_changed || out_changed) {
setup_peak_meters ();
reset_panner ();
if (in_changed || out_changed) {
setup_peak_meters ();
reset_panner ();
}
}
if (out_changed) {
@ -1213,6 +1213,7 @@ IO::ensure_inputs (uint32_t n, bool clear, bool lockit, void* src)
if (lockit) {
LockMonitor em (_session.engine().process_lock(), __LINE__, __FILE__);
LockMonitor im (io_lock, __LINE__, __FILE__);
changed = ensure_inputs_locked (n, clear, src);
} else {
changed = ensure_inputs_locked (n, clear, src);
@ -1314,6 +1315,7 @@ IO::ensure_outputs (uint32_t n, bool clear, bool lockit, void* src)
if (lockit) {
LockMonitor em (_session.engine().process_lock(), __LINE__, __FILE__);
LockMonitor im (io_lock, __LINE__, __FILE__);
changed = ensure_outputs_locked (n, clear, src);
} else {
changed = ensure_outputs_locked (n, clear, src);
@ -2406,7 +2408,7 @@ IO::setup_peak_meters ()
while (_peak_power.size() < limit) {
_peak_power.push_back (0);
_stored_peak_power.push_back (0);
_visible_peak_power.push_back (0);
}
}
@ -2436,16 +2438,35 @@ IO::send_state_changed ()
}
void
IO::grab_peak_power ()
IO::meter ()
{
LockMonitor lm (io_lock, __LINE__, __FILE__);
uint32_t limit = max (_ninputs, _noutputs);
for (uint32_t n = 0; n < limit; ++n) {
/* XXX should we use atomic exchange here ? */
_stored_peak_power[n] = _peak_power[n];
/* XXX we should use atomic exchange here */
/* grab peak since last read */
float new_peak = _peak_power[n];
_peak_power[n] = 0;
/* compute new visible value using falloff */
if (new_peak > 0.0) {
new_peak = coefficient_to_dB (new_peak);
} else {
new_peak = minus_infinity();
}
if (_session.meter_falloff() == 0.0f || new_peak > _visible_peak_power[n]) {
_visible_peak_power[n] = new_peak;
} else {
// do falloff
new_peak = _visible_peak_power[n] - _session.meter_falloff();
_visible_peak_power[n] = max (new_peak, -200.0f);
}
}
}

View File

@ -813,8 +813,8 @@ Route::add_redirect (Redirect *redirect, void *src, uint32_t* err_streams)
while (_peak_power.size() < potential_max_streams) {
_peak_power.push_back(0);
}
while (_stored_peak_power.size() < potential_max_streams) {
_stored_peak_power.push_back(0);
while (_visible_peak_power.size() < potential_max_streams) {
_visible_peak_power.push_back(0);
}
_redirects.push_back (redirect);
@ -871,8 +871,8 @@ Route::add_redirects (const RedirectList& others, void *src, uint32_t* err_strea
while (_peak_power.size() < potential_max_streams) {
_peak_power.push_back(0);
}
while (_stored_peak_power.size() < potential_max_streams) {
_stored_peak_power.push_back(0);
while (_visible_peak_power.size() < potential_max_streams) {
_visible_peak_power.push_back(0);
}
_redirects.push_back (*i);

View File

@ -154,7 +154,6 @@ void *
Session::_butler_thread_work (void* arg)
{
PBD::ThreadCreated (pthread_self(), X_("Butler"));
return ((Session *) arg)->butler_thread_work ();
return 0;
}

View File

@ -341,7 +341,7 @@ Session::second_stage_init (bool new_session)
deliver_mmc (MIDI::MachineControl::cmdMmcReset, 0);
deliver_mmc (MIDI::MachineControl::cmdLocate, 0);
ControlProtocolManager::instance().startup (*this);
ControlProtocolManager::instance().set_session (*this);
if (new_session) {
_end_location_is_free = true;

View File

@ -18,6 +18,8 @@
$Id$
*/
#include <map>
#include <gtk/gtkpaned.h>
#include <gtk/gtk.h>
@ -41,7 +43,7 @@ Gtkmm2ext::set_size_request_to_display_given_text (Gtk::Widget &w, const gchar *
int width = 0;
w.ensure_style ();
w.create_pango_layout(text)->get_pixel_size (width, height);
w.create_pango_layout (text)->get_pixel_size (width, height);
height += vpadding;
width += hpadding;

View File

@ -29,12 +29,13 @@ genericmidi.Append(CXXFLAGS="-DDATA_DIR=\\\""+final_prefix+"/share\\\"")
genericmidi.Append(CXXFLAGS="-DCONFIG_DIR=\\\""+final_config_prefix+"\\\"")
genericmidi.Append(CXXFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"")
genericmidi.Append(CPPPATH=libraries['ardour'].get ('CPPPATH', []))
genericmidi.Append(CPPPATH=libraries['sigc2'].get ('CPPPATH', []))
genericmidi.Append(CPPPATH=libraries['pbd3'].get ('CPPPATH', []))
genericmidi.Append(CPPPATH=libraries['midi++2'].get ('CPPPATH', []))
genericmidi.Merge ([
libraries['usb'],
libraries['ardour'],
libraries['sigc2'],
libraries['pbd3'],
libraries['midi++2'],
libraries['xml']
])

View File

@ -1,3 +1,5 @@
#include <midi++/port.h>
#include <ardour/route.h>
#include <ardour/session.h>
@ -10,13 +12,28 @@ using namespace ARDOUR;
GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
: ControlProtocol (s, _("GenericMIDI"))
{
_port = 0;
_port = s.midi_port();
s.MIDI_PortChanged.connect (mem_fun (*this, &GenericMidiControlProtocol::port_change));
}
GenericMidiControlProtocol::~GenericMidiControlProtocol ()
{
}
int
GenericMidiControlProtocol::init ()
{
/* start delivery/outbound thread */
return init_thread ();
}
void
GenericMidiControlProtocol::port_change ()
{
_port = session.midi_port ();
}
void
GenericMidiControlProtocol::set_port (MIDI::Port* p)
{
@ -29,20 +46,19 @@ GenericMidiControlProtocol::send_route_feedback (list<Route*>& routes)
if (_port != 0) {
const int32_t bufsize = 16 * 1024;
MIDI::byte buf[bufsize];
int32_t bsize = bufsize;
MIDI::byte* buf = new MIDI::byte[bufsize];
MIDI::byte* end = buf;
for (list<Route*>::iterator r = routes.begin(); r != routes.end(); ++r) {
end = (*r)->write_midi_feedback (end, bsize);
end = (*r)->write_midi_feedback (end, bsize);
}
if (end == buf) {
delete [] buf;
return;
}
session.deliver_midi (_port, buf, (int32_t) (end - buf));
_port->write (buf, (int32_t) (end - buf));
//cerr << "MIDI feedback: wrote " << (int32_t) (end - buf) << " to midi port\n";
}
}

View File

@ -14,6 +14,8 @@ class GenericMidiControlProtocol : public ControlProtocol {
GenericMidiControlProtocol (Session&);
virtual ~GenericMidiControlProtocol();
int init ();
bool active() const;
void set_port (MIDI::Port*);
@ -24,6 +26,8 @@ class GenericMidiControlProtocol : public ControlProtocol {
private:
void route_feedback (ARDOUR::Route&, bool);
MIDI::Port* _port;
void port_change ();
};
}

View File

@ -7,7 +7,14 @@ using namespace ARDOUR;
ControlProtocol*
new_generic_midi_protocol (ControlProtocolDescriptor* descriptor, Session* s)
{
return new GenericMidiControlProtocol (*s);
GenericMidiControlProtocol* gmcp = new GenericMidiControlProtocol (*s);
if (gmcp->init ()) {
delete gmcp;
return 0;
}
return gmcp;
}
void
@ -18,6 +25,7 @@ delete_generic_midi_protocol (ControlProtocolDescriptor* descriptor, ControlProt
static ControlProtocolDescriptor generic_midi_descriptor = {
name : "Generic MIDI",
id : "uri://ardour.org/surfaces/generic_midi:0",
ptr : 0,
module : 0,
initialize : new_generic_midi_protocol,

View File

@ -7,7 +7,15 @@ using namespace ARDOUR;
ControlProtocol*
new_tranzport_protocol (ControlProtocolDescriptor* descriptor, Session* s)
{
return new TranzportControlProtocol (*s);
TranzportControlProtocol* tcp = new TranzportControlProtocol (*s);
if (tcp->init ()) {
delete tcp;
return 0;
}
return tcp;
}
void
@ -18,10 +26,12 @@ delete_tranzport_protocol (ControlProtocolDescriptor* descriptor, ControlProtoco
static ControlProtocolDescriptor tranzport_descriptor = {
name : "Tranzport",
id : "uri://ardour.org/surfaces/tranzport:0",
ptr : 0,
module : 0,
initialize : new_tranzport_protocol,
destroy : delete_tranzport_protocol
};

View File

@ -34,6 +34,8 @@ TranzportControlProtocol::TranzportControlProtocol (Session& s)
wheel_shift_mode = WheelShiftGain;
timerclear (&last_wheel_motion);
last_wheel_dir = 1;
display_mode = DisplayNormal;
requested_display_mode = display_mode;
memset (current_screen, 0, sizeof (current_screen));
@ -60,6 +62,15 @@ TranzportControlProtocol::init ()
return -1;
}
lcd_clear ();
print (0, 0, "Welcome to");
print (1, 0, "Ardour");
show_wheel_mode();
next_track ();
show_transport_time ();
/* outbound thread */
init_thread ();
@ -85,17 +96,124 @@ TranzportControlProtocol::send_route_feedback (list<Route*>& routes)
void
TranzportControlProtocol::send_global_feedback ()
{
if (_device_status == STATUS_OFFLINE) {
if (requested_display_mode != display_mode) {
switch (requested_display_mode) {
case DisplayNormal:
enter_normal_display_mode ();
break;
case DisplayBigMeter:
enter_big_meter_mode ();
break;
}
}
switch (display_mode) {
case DisplayBigMeter:
show_meter ();
break;
case DisplayNormal:
show_transport_time ();
if (session.soloing()) {
light_on (LightAnysolo);
} else {
light_off (LightAnysolo);
}
break;
}
}
void
TranzportControlProtocol::next_display_mode ()
{
cerr << "Next display mode\n";
switch (display_mode) {
case DisplayNormal:
requested_display_mode = DisplayBigMeter;
break;
case DisplayBigMeter:
requested_display_mode = DisplayNormal;
break;
}
}
void
TranzportControlProtocol::enter_big_meter_mode ()
{
lcd_clear ();
lights_off ();
display_mode = DisplayBigMeter;
}
void
TranzportControlProtocol::enter_normal_display_mode ()
{
lcd_clear ();
lights_off ();
show_current_track ();
show_wheel_mode ();
show_transport_time ();
display_mode = DisplayNormal;
}
float
log_meter (float db)
{
float def = 0.0f; /* Meter deflection %age */
if (db < -70.0f) {
def = 0.0f;
} else if (db < -60.0f) {
def = (db + 70.0f) * 0.25f;
} else if (db < -50.0f) {
def = (db + 60.0f) * 0.5f + 2.5f;
} else if (db < -40.0f) {
def = (db + 50.0f) * 0.75f + 7.5f;
} else if (db < -30.0f) {
def = (db + 40.0f) * 1.5f + 15.0f;
} else if (db < -20.0f) {
def = (db + 30.0f) * 2.0f + 30.0f;
} else if (db < 6.0f) {
def = (db + 20.0f) * 2.5f + 50.0f;
} else {
def = 115.0f;
}
/* 115 is the deflection %age that would be
when db=6.0. this is an arbitrary
endpoint for our scaling.
*/
return def/115.0f;
}
void
TranzportControlProtocol::show_meter ()
{
if (current_route == 0) {
return;
}
show_transport_time ();
float level = current_route->peak_input_power (0);
float fraction = log_meter (level);
int fill = (int) floor (fraction * 20);
char buf[21];
int i;
if (session.soloing()) {
light_on (LightAnysolo);
} else {
light_off (LightAnysolo);
for (i = 0; i < fill; ++i) {
buf[i] = 0x70; /* tranzport special code for 4 quadrant LCD block */
}
for (; i < 20; ++i) {
buf[i] = ' ';
}
buf[21] = '\0';
print (0, 0, buf);
print (1, 0, buf);
}
void
@ -141,33 +259,6 @@ TranzportControlProtocol::thread_work ()
{
PBD::ThreadCreated (pthread_self(), X_("tranzport monitor"));
/* wait for the device to go online */
while (true) {
if (read()) {
return 0;
}
switch (_device_status) {
case STATUS_OFFLINE:
cerr << "tranzport offline\n";
break;
case STATUS_ONLINE:
case 0:
cerr << "tranzport online\n";
break;
default:
cerr << "tranzport: unknown status\n";
break;
}
if (_device_status == STATUS_ONLINE || _device_status == 0) {
break;
}
}
lcd_clear ();
show_wheel_mode();
while (true) {
if (read ()) {
break;
@ -217,6 +308,13 @@ TranzportControlProtocol::open_core (struct usb_device* dev)
return -1;
}
if (usb_set_configuration (udev, 1) < 0) {
error << _("Tranzport: cannot configure USB interface") << endmsg;
usb_close (udev);
udev = 0;
return -1;
}
return 0;
}
@ -276,7 +374,8 @@ TranzportControlProtocol::lcd_clear ()
cmd[7] = 0x00;
{
LockMonitor lm (write_lock, __LINE__, __FILE__);
LockMonitor lp (print_lock, __LINE__, __FILE__);
LockMonitor lw (write_lock, __LINE__, __FILE__);
for (uint8_t i = 0; i < 10; ++i) {
cmd[2] = i;
@ -287,35 +386,16 @@ TranzportControlProtocol::lcd_clear ()
}
}
int
TranzportControlProtocol::lcd_write (int row, int col, uint8_t cell, const char* text)
void
TranzportControlProtocol::lights_off ()
{
uint8_t cmd[8];
if (cell > 9) {
return -1;
}
if (memcmp (text, &current_screen[row][col], 4)) {
current_screen[row][col] = text[0];
current_screen[row][col+1] = text[1];
current_screen[row][col+2] = text[2];
current_screen[row][col+3] = text[3];
cmd[0] = 0x00;
cmd[1] = 0x01;
cmd[2] = cell;
cmd[3] = text[0];
cmd[4] = text[1];
cmd[5] = text[2];
cmd[6] = text[3];
cmd[7] = 0x00;
return write (cmd, 500);
}
return 0;
light_off (LightRecord);
light_off (LightTrackrec);
light_off (LightTrackmute);
light_off (LightTracksolo);
light_off (LightAnysolo);
light_off (LightLoop);
light_off (LightPunch);
}
int
@ -865,7 +945,11 @@ TranzportControlProtocol::button_event_fastforward_release (bool shifted)
void
TranzportControlProtocol::button_event_stop_press (bool shifted)
{
session.request_transport_speed (0.0);
if (shifted) {
next_display_mode ();
} else {
session.request_transport_speed (0.0);
}
}
void
@ -1039,9 +1123,17 @@ void
TranzportControlProtocol::shuttle ()
{
if (_datawheel < WheelDirectionThreshold) {
session.request_transport_speed (session.transport_speed() + 0.1);
if (session.transport_speed() < 0) {
session.request_transport_speed (1.0);
} else {
session.request_transport_speed (session.transport_speed() + 0.1);
}
} else {
session.request_transport_speed (session.transport_speed() - 0.1);
if (session.transport_speed() > 0) {
session.request_transport_speed (-1.0);
} else {
session.request_transport_speed (session.transport_speed() - 0.1);
}
}
}
@ -1254,23 +1346,46 @@ TranzportControlProtocol::print (int row, int col, const char *text)
int offset = col % 4;
/* copy current cell contents into tmp */
{
memcpy (tmp, &current_screen[row][base_col], 4);
LockMonitor lm (print_lock, __LINE__, __FILE__);
/* overwrite with new text */
/* copy current cell contents into tmp */
memcpy (tmp, &current_screen[row][base_col], 4);
/* overwrite with new text */
uint32_t tocopy = min ((4U - offset), left);
uint32_t tocopy = min ((4U - offset), left);
memcpy (tmp+offset, text, tocopy);
memcpy (tmp+offset, text, tocopy);
cell += (row * 5);
uint8_t cmd[8];
lcd_write (row, base_col, cell, tmp);
text += tocopy;
left -= tocopy;
col += tocopy;
/* 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);
}
text += tocopy;
left -= tocopy;
col += tocopy;
}
}
}

View File

@ -77,6 +77,11 @@ class TranzportControlProtocol : public ControlProtocol {
WheelScrub,
WheelShuttle
};
enum DisplayMode {
DisplayNormal,
DisplayBigMeter
};
pthread_t thread;
uint32_t buttonmask;
@ -92,6 +97,8 @@ class TranzportControlProtocol : public ControlProtocol {
WheelShiftMode wheel_shift_mode;
struct timeval last_wheel_motion;
int last_wheel_dir;
DisplayMode display_mode;
DisplayMode requested_display_mode;
std::vector<sigc::connection> track_connections;
@ -103,6 +110,7 @@ class TranzportControlProtocol : public ControlProtocol {
jack_nframes_t last_where;
PBD::Lock write_lock;
PBD::Lock print_lock;
int open ();
int read (uint32_t timeout_override = 0);
@ -112,16 +120,21 @@ class TranzportControlProtocol : public ControlProtocol {
int open_core (struct usb_device*);
void lcd_clear ();
int lcd_write (int row, int col, uint8_t cell, const char *text);
void print (int row, int col, const char* text);
int light_on (LightID);
int light_off (LightID);
void lights_off ();
void enter_big_meter_mode ();
void enter_normal_display_mode ();
void next_display_mode ();
void show_current_track ();
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*);