WebSockets: notify server there are pending client updates
This is an update to the surface ArdourFeedback class that is needed to support the new event loop integration method. The various session event callbacks cannot be queued in the surface event loop because that would create a delay between the time such events are fired and the time for writing to clients arrive, due to lws_service() blocking while it waits to read. To solve this issue a helper AbstractUI is created for catching events as soon as possible and issuing a call to lws_cancel_service(). See WebsocketsServer::glib_idle_callback()
This commit is contained in:
parent
b3661af92a
commit
e3569b6469
@ -39,8 +39,8 @@ ArdourWebsockets::ArdourWebsockets (Session& s)
|
||||
, AbstractUI<ArdourWebsocketsUIRequest> (name ())
|
||||
, _mixer (*this)
|
||||
, _transport (*this)
|
||||
, _feedback (*this)
|
||||
, _server (*this)
|
||||
, _feedback (*this)
|
||||
, _dispatcher (*this)
|
||||
{
|
||||
_components.push_back (&_mixer);
|
||||
|
@ -95,8 +95,8 @@ protected:
|
||||
private:
|
||||
ArdourMixer _mixer;
|
||||
ArdourTransport _transport;
|
||||
ArdourFeedback _feedback;
|
||||
WebsocketsServer _server;
|
||||
ArdourFeedback _feedback;
|
||||
WebsocketsDispatcher _dispatcher;
|
||||
std::vector<SurfaceComponent*> _components;
|
||||
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include "ardour/session.h"
|
||||
#include "ardour/tempo.h"
|
||||
|
||||
#include "pbd/abstract_ui.cc" // instantiate template
|
||||
|
||||
#include "feedback.h"
|
||||
#include "transport.h"
|
||||
#include "server.h"
|
||||
@ -28,9 +30,20 @@
|
||||
// TO DO: make this configurable
|
||||
#define POLL_INTERVAL_MS 100
|
||||
|
||||
#define OPTIONAL_CONNECT_HELPER(s,c) if (server ().should_request_write ()) \
|
||||
s.connect (c, MISSING_INVALIDATOR, boost::bind<void> \
|
||||
(ServerWriteObserver (), &server ()), &_helper);
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace ArdourSurface;
|
||||
|
||||
struct ServerWriteObserver {
|
||||
void operator() (WebsocketsServer *server)
|
||||
{
|
||||
server->request_write ();
|
||||
}
|
||||
};
|
||||
|
||||
struct TransportObserver {
|
||||
void operator() (ArdourFeedback* p)
|
||||
{
|
||||
@ -97,6 +110,24 @@ struct PluginParamValueObserver {
|
||||
}
|
||||
};
|
||||
|
||||
FeedbackHelperUI::FeedbackHelperUI()
|
||||
: AbstractUI<BaseUI::BaseRequestObject> ("feedback_helper")
|
||||
{
|
||||
pthread_set_name ("test_ui_thread"); // FIXME - needed?
|
||||
run_loop_thread = Glib::Threads::Thread::self ();
|
||||
set_event_loop_for_thread (this);
|
||||
ARDOUR::SessionEvent::create_per_thread_pool ("test", 512); // FIXME - needed?
|
||||
}
|
||||
|
||||
void
|
||||
FeedbackHelperUI::do_request (BaseUI::BaseRequestObject* req) {
|
||||
if (req->type == CallSlot) {
|
||||
call_slot (MISSING_INVALIDATOR, req->the_slot);
|
||||
} else if (req->type == Quit) {
|
||||
quit ();
|
||||
}
|
||||
};
|
||||
|
||||
int
|
||||
ArdourFeedback::start ()
|
||||
{
|
||||
@ -107,7 +138,15 @@ ArdourFeedback::start ()
|
||||
Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (POLL_INTERVAL_MS);
|
||||
_periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this,
|
||||
&ArdourFeedback::poll));
|
||||
periodic_timeout->attach (main_loop ()->get_context ());
|
||||
|
||||
// server must be started before feedback otherwise
|
||||
// should_request_write() will always return false
|
||||
if (!server ().should_request_write ()) {
|
||||
periodic_timeout->attach (main_loop ()->get_context ());
|
||||
} else {
|
||||
_helper.run();
|
||||
periodic_timeout->attach (_helper.main_loop()->get_context ());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -115,6 +154,10 @@ ArdourFeedback::start ()
|
||||
int
|
||||
ArdourFeedback::stop ()
|
||||
{
|
||||
if (server ().should_request_write ()) {
|
||||
_helper.quit();
|
||||
}
|
||||
|
||||
_periodic_connection.disconnect ();
|
||||
_transport_connections.drop_connections ();
|
||||
|
||||
@ -176,6 +219,10 @@ ArdourFeedback::poll () const
|
||||
update_all (Node::strip_meter, it->first, db);
|
||||
}
|
||||
|
||||
if (server ().should_request_write ()) {
|
||||
server ().request_write ();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -185,10 +232,15 @@ ArdourFeedback::observe_transport ()
|
||||
ARDOUR::Session& sess = session ();
|
||||
sess.TransportStateChange.connect (_transport_connections, MISSING_INVALIDATOR,
|
||||
boost::bind<void> (TransportObserver (), this), event_loop ());
|
||||
OPTIONAL_CONNECT_HELPER(sess.TransportStateChange, _transport_connections);
|
||||
|
||||
sess.RecordStateChanged.connect (_transport_connections, MISSING_INVALIDATOR,
|
||||
boost::bind<void> (RecordStateObserver (), this), event_loop ());
|
||||
OPTIONAL_CONNECT_HELPER(sess.RecordStateChanged, _transport_connections);
|
||||
|
||||
sess.tempo_map ().PropertyChanged.connect (_transport_connections, MISSING_INVALIDATOR,
|
||||
boost::bind<void> (TempoObserver (), this), event_loop ());
|
||||
OPTIONAL_CONNECT_HELPER(sess.tempo_map ().PropertyChanged, _transport_connections);
|
||||
}
|
||||
|
||||
void
|
||||
@ -202,14 +254,17 @@ ArdourFeedback::observe_mixer ()
|
||||
|
||||
stripable->gain_control ()->Changed.connect (*it->second, MISSING_INVALIDATOR,
|
||||
boost::bind<void> (StripGainObserver (), this, strip_id), event_loop ());
|
||||
OPTIONAL_CONNECT_HELPER(stripable->gain_control ()->Changed, *it->second);
|
||||
|
||||
if (stripable->pan_azimuth_control ()) {
|
||||
stripable->pan_azimuth_control ()->Changed.connect (*it->second, MISSING_INVALIDATOR,
|
||||
boost::bind<void> (StripPanObserver (), this, strip_id), event_loop ());
|
||||
OPTIONAL_CONNECT_HELPER(stripable->pan_azimuth_control ()->Changed, *it->second);
|
||||
}
|
||||
|
||||
stripable->mute_control ()->Changed.connect (*it->second, MISSING_INVALIDATOR,
|
||||
boost::bind<void> (StripMuteObserver (), this, strip_id), event_loop ());
|
||||
OPTIONAL_CONNECT_HELPER(stripable->mute_control ()->Changed, *it->second);
|
||||
|
||||
observe_strip_plugins (strip_id, strip->plugins ());
|
||||
}
|
||||
@ -229,6 +284,7 @@ ArdourFeedback::observe_strip_plugins (uint32_t strip_id, ArdourMixerStrip::Plug
|
||||
if (control) {
|
||||
control->Changed.connect (*plugin, MISSING_INVALIDATOR,
|
||||
boost::bind<void> (PluginBypassObserver (), this, strip_id, plugin_id), event_loop ());
|
||||
OPTIONAL_CONNECT_HELPER(control->Changed, *plugin);
|
||||
}
|
||||
|
||||
for (uint32_t param_id = 0; param_id < plugin->param_count (); ++param_id) {
|
||||
@ -239,6 +295,7 @@ ArdourFeedback::observe_strip_plugins (uint32_t strip_id, ArdourMixerStrip::Plug
|
||||
boost::bind<void> (PluginParamValueObserver (), this, strip_id, plugin_id, param_id,
|
||||
boost::weak_ptr<AutomationControl>(control)),
|
||||
event_loop ());
|
||||
OPTIONAL_CONNECT_HELPER(control->Changed, *plugin);
|
||||
} catch (ArdourMixerNotFoundException& e) {
|
||||
/* ignore */
|
||||
}
|
||||
|
@ -21,7 +21,9 @@
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <glibmm/main.h>
|
||||
#include <glibmm/threads.h>
|
||||
|
||||
#include "pbd/abstract_ui.h"
|
||||
|
||||
#include "component.h"
|
||||
#include "typed_value.h"
|
||||
@ -29,6 +31,17 @@
|
||||
|
||||
namespace ArdourSurface {
|
||||
|
||||
class FeedbackHelperUI : public AbstractUI<BaseUI::BaseRequestObject>
|
||||
{
|
||||
public:
|
||||
FeedbackHelperUI ();
|
||||
~FeedbackHelperUI () {};
|
||||
|
||||
protected:
|
||||
virtual void do_request (BaseUI::BaseRequestObject*);
|
||||
|
||||
};
|
||||
|
||||
class ArdourFeedback : public SurfaceComponent
|
||||
{
|
||||
public:
|
||||
@ -49,6 +62,9 @@ private:
|
||||
PBD::ScopedConnectionList _transport_connections;
|
||||
sigc::connection _periodic_connection;
|
||||
|
||||
// Only needed for server event loop integration method 3
|
||||
FeedbackHelperUI _helper;
|
||||
|
||||
bool poll () const;
|
||||
|
||||
void observe_transport ();
|
||||
|
@ -195,6 +195,7 @@ WebsocketsServer::stop ()
|
||||
|
||||
if (_g_source) { // Method 3
|
||||
g_source_destroy (_g_source);
|
||||
lws_cancel_service (_lws_context);
|
||||
}
|
||||
|
||||
if (_lws_context) {
|
||||
|
@ -112,8 +112,11 @@ private:
|
||||
|
||||
GSource* _g_source;
|
||||
|
||||
void request_write ();
|
||||
static gboolean glib_idle_callback (void *data);
|
||||
static gboolean glib_idle_callback (void *);
|
||||
|
||||
public:
|
||||
bool should_request_write () { return _g_source != 0; }
|
||||
void request_write ();
|
||||
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user