summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/surfaces/websockets/dispatcher.cc11
-rw-r--r--libs/surfaces/websockets/server.cc6
-rw-r--r--share/web_surfaces/builtin/mixer-demo/css/main.css5
-rw-r--r--share/web_surfaces/builtin/mixer-demo/js/main.js143
-rw-r--r--share/web_surfaces/builtin/mixer-demo/js/widget.js46
-rw-r--r--share/web_surfaces/shared/ardour.js55
-rw-r--r--share/web_surfaces/shared/callback.js28
-rw-r--r--share/web_surfaces/shared/control.js12
-rw-r--r--share/web_surfaces/shared/message.js24
9 files changed, 188 insertions, 142 deletions
diff --git a/libs/surfaces/websockets/dispatcher.cc b/libs/surfaces/websockets/dispatcher.cc
index b497c24081..62f64268aa 100644
--- a/libs/surfaces/websockets/dispatcher.cc
+++ b/libs/surfaces/websockets/dispatcher.cc
@@ -29,13 +29,13 @@ using namespace ARDOUR;
#define NODE_METHOD_PAIR(x) (Node::x, &WebsocketsDispatcher::x##_handler)
WebsocketsDispatcher::NodeMethodMap
- WebsocketsDispatcher::_node_to_method = boost::assign::map_list_of
+ WebsocketsDispatcher::_node_to_method = boost::assign::map_list_of
NODE_METHOD_PAIR (tempo)
NODE_METHOD_PAIR (strip_gain)
- NODE_METHOD_PAIR (strip_pan)
- NODE_METHOD_PAIR (strip_mute)
- NODE_METHOD_PAIR (strip_plugin_enable)
- NODE_METHOD_PAIR (strip_plugin_param_value);
+ NODE_METHOD_PAIR (strip_pan)
+ NODE_METHOD_PAIR (strip_mute)
+ NODE_METHOD_PAIR (strip_plugin_enable)
+ NODE_METHOD_PAIR (strip_plugin_param_value);
void
WebsocketsDispatcher::dispatch (Client client, const NodeStateMessage& msg)
@@ -105,7 +105,6 @@ WebsocketsDispatcher::update_all_nodes (Client client)
val.push_back (std::string ("i"));
val.push_back (pd.lower);
val.push_back (pd.upper);
- val.push_back (pd.integer_step);
} else {
val.push_back (std::string ("d"));
val.push_back (pd.lower);
diff --git a/libs/surfaces/websockets/server.cc b/libs/surfaces/websockets/server.cc
index 7f52b261de..8bd7eb178b 100644
--- a/libs/surfaces/websockets/server.cc
+++ b/libs/surfaces/websockets/server.cc
@@ -92,10 +92,10 @@ WebsocketsServer::WebsocketsServer (ArdourSurface::ArdourWebsockets& surface)
#if LWS_LIBRARY_VERSION_MAJOR < 3
/* older libwebsockets does not define mime type for svg files */
memset (&_lws_vhost_opt, 0, sizeof (lws_protocol_vhost_options));
- _lws_vhost_opt.name = ".svg";
- _lws_vhost_opt.value = "image/svg+xml";
+ _lws_vhost_opt.name = ".svg";
+ _lws_vhost_opt.value = "image/svg+xml";
_lws_mnt_index.extra_mimetypes = &_lws_vhost_opt;
- _lws_mnt_user.extra_mimetypes = &_lws_vhost_opt;
+ _lws_mnt_user.extra_mimetypes = &_lws_vhost_opt;
#endif
}
diff --git a/share/web_surfaces/builtin/mixer-demo/css/main.css b/share/web_surfaces/builtin/mixer-demo/css/main.css
index 0fd812c39b..23965c3fd0 100644
--- a/share/web_surfaces/builtin/mixer-demo/css/main.css
+++ b/share/web_surfaces/builtin/mixer-demo/css/main.css
@@ -26,6 +26,7 @@ div {
flex: 1;
display: flex;
flex-direction: column;
+ overflow: hidden;
box-shadow: 0px 0px 10px #000;
}
@@ -76,6 +77,10 @@ div {
color: rgb(172,128,255);
}
+.info {
+ color: rgb(99,208,230);
+}
+
.error {
color: rgb(249,36,114);
}
diff --git a/share/web_surfaces/builtin/mixer-demo/js/main.js b/share/web_surfaces/builtin/mixer-demo/js/main.js
index 64d5fca33f..b6713664b5 100644
--- a/share/web_surfaces/builtin/mixer-demo/js/main.js
+++ b/share/web_surfaces/builtin/mixer-demo/js/main.js
@@ -17,8 +17,7 @@
*/
// This example does not call the API methods in ardour.js,
- // instead it interacts at a lower level by coupling the widgets
- // tightly to the message stream
+ // instead it couples the widgets directly to the message stream
import { ANode, Message } from '/shared/message.js';
import { ArdourClient } from '/shared/ardour.js';
@@ -29,8 +28,6 @@ import { Switch, DiscreteSlider, ContinuousSlider, LogarithmicSlider,
(() => {
const MAX_LOG_LINES = 1000;
- const FEEDBACK_NODES = [ANode.STRIP_GAIN, ANode.STRIP_PAN, ANode.STRIP_METER,
- ANode.STRIP_PLUGIN_ENABLE, ANode.STRIP_PLUGIN_PARAM_VALUE];
const ardour = new ArdourClient(location.host);
const widgets = {};
@@ -43,101 +40,95 @@ import { Switch, DiscreteSlider, ContinuousSlider, LogarithmicSlider,
div.innerHTML = `${manifest.name.toUpperCase()} v${manifest.version} — ${manifest.description}`;
});
- ardour.addCallback({
- onMessage: (msg) => {
- log(`↙ ${msg}`, 'message-in');
-
- if (msg.node == ANode.STRIP_DESC) {
- createStrip (msg.addr, ...msg.val);
- } else if (msg.node == ANode.STRIP_PLUGIN_DESC) {
- createStripPlugin (msg.addr, ...msg.val);
- } else if (msg.node == ANode.STRIP_PLUGIN_PARAM_DESC) {
- createStripPluginParam (msg.addr, ...msg.val);
- } else if (FEEDBACK_NODES.includes(msg.node)) {
- if (widgets[msg.hash]) {
- widgets[msg.hash].value = msg.val[0];
- }
- }
- },
-
- onError: () => {
- log('Client error', 'error');
- }
+ ardour.addCallbacks({
+ onConnected: (error) => { log('Client connected', 'info'); },
+ onDisconnected: (error) => { log('Client disconnected', 'error'); },
+ onMessage: processMessage,
+ onStripDesc: createStrip,
+ onStripPluginDesc: createStripPlugin,
+ onStripPluginParamDesc: createStripPluginParam
});
- ardour.open();
+ ardour.connect();
}
- function createStrip (addr, name) {
- const id = `strip-${addr[0]}`;
+ function createStrip (stripId, name) {
+ const domId = `strip-${stripId}`;
+ if (document.getElementById(domId) != null) {
+ return;
+ }
+
const strips = document.getElementById('strips');
- const div = createElem(`<div class="strip" id="${id}"></div>`, strips);
- createElem(`<label class="comp-name" for="${id}">∿&emsp;&emsp;${name}</label>`, div);
+ const div = createElem(`<div class="strip" id="${domId}"></div>`, strips);
+ createElem(`<label class="comp-name" for="${domId}">∿&emsp;&emsp;${name}</label>`, div);
// meter
- const meter = new StripMeter(ANode.STRIP_METER, addr);
+ const meter = new StripMeter();
meter.el.classList.add('slider-meter');
- meter.attach(div);
- register(meter);
+ meter.appendTo(div);
+ connectWidget(meter, ANode.STRIP_METER, stripId);
// gain
let holder = createElem(`<div class="strip-slider"></div>`, div);
createElem(`<label>Gain</label>`, holder);
- const gain = new StripGainSlider(ANode.STRIP_GAIN, addr);
- gain.attach(holder, (val) => send(gain));
- register(gain);
+ const gain = new StripGainSlider();
+ gain.appendTo(holder);
+ connectWidget(gain, ANode.STRIP_GAIN, stripId);
// pan
holder = createElem(`<div class="strip-slider"></div>`, div);
createElem(`<label>Pan</label>`, holder);
- const pan = new StripPanSlider(ANode.STRIP_PAN, addr);
- pan.attach(holder, (val) => send(pan));
- register(pan);
+ const pan = new StripPanSlider();
+ pan.appendTo(holder);
+ connectWidget(pan, ANode.STRIP_PAN, stripId);
}
- function createStripPlugin (addr, name) {
- const strip = document.getElementById(`strip-${addr[0]}`);
- const id = `plugin-${addr[0]}-${addr[1]}`;
- const div = createElem(`<div class="plugin" id="${id}"></div>`, strip);
+ function createStripPlugin (stripId, pluginId, name) {
+ const domId = `plugin-${stripId}-${pluginId}`;
+ if (document.getElementById(domId) != null) {
+ return;
+ }
+
+ const strip = document.getElementById(`strip-${stripId}`);
+ const div = createElem(`<div class="plugin" id="${domId}"></div>`, strip);
createElem(`<label class="comp-name">⨍&emsp;&emsp;${name}</label>`, div);
- const enable = new Switch(ANode.STRIP_PLUGIN_ENABLE, addr);
+
+ const enable = new Switch();
enable.el.classList.add('plugin-enable');
- enable.attach(div, (val) => send(enable));
- register(enable);
+ enable.appendTo(div);
+ connectWidget(enable, ANode.STRIP_PLUGIN_ENABLE, stripId, pluginId);
}
- function createStripPluginParam (addr, name, dataType, min, max, isLog) {
+ function createStripPluginParam (stripId, pluginId, paramId, name, valueType, min, max, isLog) {
+ const domId = `param-${stripId}-${pluginId}-${paramId}`;
+ if (document.getElementById(domId) != null) {
+ return;
+ }
+
let param, cssClass;
- if (dataType == 'b') {
+ if (valueType == 'b') {
cssClass = 'boolean';
- param = new Switch(ANode.STRIP_PLUGIN_PARAM_VALUE, addr);
- } else if (dataType == 'i') {
+ param = new Switch();
+ } else if (valueType == 'i') {
cssClass = 'discrete';
- param = new DiscreteSlider(ANode.STRIP_PLUGIN_PARAM_VALUE, addr, min, max);
- } else if (dataType == 'd') {
+ param = new DiscreteSlider(min, max);
+ } else if (valueType == 'd') {
cssClass = 'continuous';
if (isLog) {
- param = new LogarithmicSlider(ANode.STRIP_PLUGIN_PARAM_VALUE, addr, min, max);
+ param = new LogarithmicSlider(min, max);
} else {
- param = new ContinuousSlider(ANode.STRIP_PLUGIN_PARAM_VALUE, addr, min, max);
+ param = new ContinuousSlider(min, max);
}
}
- const plugin = document.getElementById(`plugin-${addr[0]}-${addr[1]}`);
- const id = `param-${addr[0]}-${addr[1]}-${addr[2]}`;
- const div = createElem(`<div class="plugin-param ${cssClass}" id="${id}"></div>`, plugin);
- createElem(`<label for="${id}">${name}</label>`, div);
+ const plugin = document.getElementById(`plugin-${stripId}-${pluginId}`);
+ const div = createElem(`<div class="plugin-param ${cssClass}" id="${domId}"></div>`, plugin);
+ createElem(`<label for="${domId}">${name}</label>`, div);
- param.attach(div, (val) => send(param));
- param.el.name = id;
- register(param);
- }
-
- function send (widget) {
- const msg = new Message(widget.node, widget.addr, [widget.value]);
- log(`↗ ${msg}`, 'message-out');
- ardour.send(msg);
+ param.el.name = domId;
+ param.appendTo(div);
+ connectWidget(param, ANode.STRIP_PLUGIN_PARAM_VALUE, stripId, pluginId, paramId);
}
function createElem (html, parent) {
@@ -153,8 +144,24 @@ import { Switch, DiscreteSlider, ContinuousSlider, LogarithmicSlider,
return elem;
}
- function register (widget) {
- widgets[widget.hash] = widget;
+ function connectWidget (widget, node, ...addr) {
+ const nodeAddrId = Message.nodeAddrId(node, addr);
+
+ widgets[nodeAddrId] = widget;
+
+ widget.callback = (val) => {
+ const msg = new Message(node, addr, [val]);
+ log(`↗ ${msg}`, 'message-out');
+ ardour.send(msg);
+ };
+ }
+
+ function processMessage (msg) {
+ log(`↙ ${msg}`, 'message-in');
+
+ if (widgets[msg.nodeAddrId]) {
+ widgets[msg.nodeAddrId].value = msg.val[0];
+ }
}
function log (message, className) {
diff --git a/share/web_surfaces/builtin/mixer-demo/js/widget.js b/share/web_surfaces/builtin/mixer-demo/js/widget.js
index 335e3d8756..417c0c7c7b 100644
--- a/share/web_surfaces/builtin/mixer-demo/js/widget.js
+++ b/share/web_surfaces/builtin/mixer-demo/js/widget.js
@@ -20,36 +20,26 @@ import { Message } from '/shared/message.js';
export class Widget {
- constructor (node, addr, html) {
- this.node = node;
- this.addr = addr;
+ constructor (html) {
const template = document.createElement('template');
template.innerHTML = html;
this.el = template.content.firstChild;
}
- attach (parent, callback) {
+ appendTo (parent) {
parent.appendChild(this.el);
-
- if (callback) {
- this.callback = callback;
- }
}
callback (value) {
// do nothing by default
}
- get hash () {
- return Message.hash(this.node, this.addr);
- }
-
}
export class Switch extends Widget {
- constructor (node, addr) {
- super (node, addr, `<input type="checkbox" class="widget-switch">`);
+ constructor () {
+ super (`<input type="checkbox" class="widget-switch">`);
this.el.addEventListener('input', (ev) => this.callback(this.value));
}
@@ -65,10 +55,10 @@ export class Switch extends Widget {
export class Slider extends Widget {
- constructor (node, addr, min, max, step) {
+ constructor (min, max, step) {
const html = `<input type="range" class="widget-slider"
min="${min}" max="${max}" step="${step}">`;
- super(node, addr, html);
+ super(html);
this.min = min;
this.max = max;
this.el.addEventListener('input', (ev) => this.callback(this.value));
@@ -86,24 +76,24 @@ export class Slider extends Widget {
export class DiscreteSlider extends Slider {
- constructor (node, addr, min, max) {
- super(node, addr, min, max, 1);
+ constructor (min, max, step) {
+ super(min, max, step || 1);
}
}
export class ContinuousSlider extends Slider {
- constructor (node, addr, min, max) {
- super(node, addr, min, max, 0.001);
+ constructor (min, max) {
+ super(min, max, 0.001);
}
}
export class LogarithmicSlider extends ContinuousSlider {
- constructor (node, addr, min, max) {
- super(node, addr, 0, 1.0);
+ constructor (min, max) {
+ super(0, 1.0);
this.minVal = Math.log(min);
this.maxVal = Math.log(max);
this.scale = this.maxVal - this.minVal;
@@ -121,16 +111,16 @@ export class LogarithmicSlider extends ContinuousSlider {
export class StripPanSlider extends ContinuousSlider {
- constructor (node, addr) {
- super(node, addr, -1.0, 1.0);
+ constructor () {
+ super(-1.0, 1.0);
}
}
export class StripGainSlider extends ContinuousSlider {
- constructor (node, addr) {
- super(node, addr, 0, 1.0)
+ constructor () {
+ super(0, 1.0)
this.minVal = -58.0;
this.maxVal = 6.0;
this.scale = (this.maxVal - this.minVal);
@@ -148,8 +138,8 @@ export class StripGainSlider extends ContinuousSlider {
export class StripMeter extends Widget {
- constructor (node, addr) {
- super(node, addr, `<label></label>`);
+ constructor () {
+ super(`<label></label>`);
}
set value (val) {
diff --git a/share/web_surfaces/shared/ardour.js b/share/web_surfaces/shared/ardour.js
index a7e6978ba2..63a548e174 100644
--- a/share/web_surfaces/shared/ardour.js
+++ b/share/web_surfaces/shared/ardour.js
@@ -16,17 +16,19 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-import { MetadataMixin } from './metadata.js';
import { ControlMixin } from './control.js';
+import { MetadataMixin } from './metadata.js';
import { Message } from './message.js';
import { MessageChannel } from './channel.js';
-// See *Mixin for the available APIs
+// See ControlMixin and MetadataMixin for available APIs
+// See ArdourCallback for an example callback implementation
class BaseArdourClient {
constructor () {
this._callbacks = [];
+ this._connected = false;
this._pendingRequest = null;
this._channel = new MessageChannel(location.host);
@@ -39,21 +41,30 @@ class BaseArdourClient {
};
}
- addCallback (callback) {
- this._callbacks.push(callback);
+ addCallbacks (callbacks) {
+ this._callbacks.push(callbacks);
}
- async open () {
- this._channel.onClose = () => {
- this._fireCallbacks('error', new Error('Message channel unexpectedly closed'));
+ async connect (autoReconnect) {
+ this._channel.onClose = async () => {
+ if (this._connected) {
+ this._fireCallbacks('disconnected');
+ this._connected = false;
+ }
+
+ if ((autoReconnect == null) || autoReconnect) {
+ await this._sleep(1000);
+ await this._connect();
+ }
};
- await this._channel.open();
+ this._connect();
}
- close () {
+ disconnect () {
this._channel.onClose = () => {};
this._channel.close();
+ this._connected = false;
}
send (msg) {
@@ -61,6 +72,12 @@ class BaseArdourClient {
}
// Private methods
+
+ async _connect () {
+ await this._channel.open();
+ this._connected = true;
+ this._fireCallbacks('connected');
+ }
_send (node, addr, val) {
const msg = new Message(node, addr, val);
@@ -75,6 +92,10 @@ class BaseArdourClient {
});
}
+ async _sendRecvSingle (node, addr, val) {
+ return await this._sendAndReceive (node, addr, val)[0];
+ }
+
_onChannelMessage (msg) {
if (this._pendingRequest && (this._pendingRequest.hash == msg.hash)) {
this._pendingRequest.resolve(msg.val);
@@ -91,9 +112,9 @@ class BaseArdourClient {
return s[0].toUpperCase() + s.slice(1).toLowerCase();
}).join('');
- for (const callback of this._callbacks) {
- if (method in callback) {
- callback[method](...args)
+ for (const callbacks of this._callbacks) {
+ if (method in callbacks) {
+ callbacks[method](...args)
}
}
}
@@ -102,15 +123,19 @@ class BaseArdourClient {
return new Error(`HTTP response status ${status}`);
}
+ async _sleep (t) {
+ return new Promise(resolve => setTimeout(resolve, 1000));
+ }
+
}
export class ArdourClient extends mixin(BaseArdourClient, ControlMixin, MetadataMixin) {}
function mixin (dstClass, ...classes) {
for (const srcClass of classes) {
- for (const methName of Object.getOwnPropertyNames(srcClass.prototype)) {
- if (methName != 'constructor') {
- dstClass.prototype[methName] = srcClass.prototype[methName];
+ for (const propName of Object.getOwnPropertyNames(srcClass.prototype)) {
+ if (propName != 'constructor') {
+ dstClass.prototype[propName] = srcClass.prototype[propName];
}
}
}
diff --git a/share/web_surfaces/shared/callback.js b/share/web_surfaces/shared/callback.js
index a510b118cf..9da8b420d2 100644
--- a/share/web_surfaces/shared/callback.js
+++ b/share/web_surfaces/shared/callback.js
@@ -20,14 +20,34 @@
export class ArdourCallback {
+ // Connection status
+ onConnected () {}
+ onDisconnected () {}
+
+ // All messages and errors
+ onMessage (msg) {}
+ onError (error) {}
+
+ // Globals
onTempo (bpm) {}
+
+ // Strips
+ onStripDesc (stripId, name) {}
+ onStripMeter (stripId, db) {}
onStripGain (stripId, db) {}
onStripPan (stripId, value) {}
onStripMute (stripId, value) {}
+
+ // Strip plugins
+ onStripPluginDesc (stripId, pluginId, name) {}
onStripPluginEnable (stripId, pluginId, value) {}
- onStripPluginParamValue (stripId, pluginId, paramId, value) {}
- onMessage (msg) {}
- onError (error) {}
+ // Strip plugin parameters
+ // valueType
+ // 'b' : boolean
+ // 'i' : integer
+ // 'd' : double
+ onStripPluginParamDesc (stripId, pluginId, paramId, name, valueType, min, max, isLog) {}
+ onStripPluginParamValue (stripId, pluginId, paramId, value) {}
- }
+}
diff --git a/share/web_surfaces/shared/control.js b/share/web_surfaces/shared/control.js
index 19116bf4c7..94ffa0a83d 100644
--- a/share/web_surfaces/shared/control.js
+++ b/share/web_surfaces/shared/control.js
@@ -23,27 +23,27 @@ import { ANode } from './message.js';
export class ControlMixin {
async getTempo () {
- return (await this._sendAndReceive(ANode.TEMPO))[0];
+ return await this._sendRecvSingle(ANode.TEMPO);
}
async getStripGain (stripId) {
- return (await this._sendAndReceive(ANode.STRIP_GAIN, [stripId]))[0];
+ return await this._sendRecvSingle(ANode.STRIP_GAIN, [stripId]);
}
async getStripPan (stripId) {
- return (await this._sendAndReceive(ANode.STRIP_PAN, [stripId]))[0];
+ return await this._sendRecvSingle(ANode.STRIP_PAN, [stripId]);
}
async getStripMute (stripId) {
- return (await this._sendAndReceive(ANode.STRIP_MUTE, [stripId]))[0];
+ return await this._sendRecvSingle(ANode.STRIP_MUTE, [stripId]);
}
async getStripPluginEnable (stripId, pluginId) {
- return (await this._sendAndReceive(ANode.STRIP_PLUGIN_ENABLE, [stripId, pluginId]))[0];
+ return await this._sendRecvSingle(ANode.STRIP_PLUGIN_ENABLE, [stripId, pluginId]);
}
async getStripPluginParamValue (stripId, pluginId, paramId) {
- return (await this._sendAndReceive(ANode.STRIP_PLUGIN_PARAM_VALUE, [stripId, pluginId, paramId]))[0];
+ return await this._sendRecvSingle(ANode.STRIP_PLUGIN_PARAM_VALUE, [stripId, pluginId, paramId]);
}
setTempo (bpm) {
diff --git a/share/web_surfaces/shared/message.js b/share/web_surfaces/shared/message.js
index 6c81cd972f..0252c9fa63 100644
--- a/share/web_surfaces/shared/message.js
+++ b/share/web_surfaces/shared/message.js
@@ -19,15 +19,15 @@
export const JSON_INF = 1.0e+128;
export const ANode = Object.freeze({
- TEMPO: 'tempo',
- STRIP_DESC: 'strip_desc',
- STRIP_METER: 'strip_meter',
- STRIP_GAIN: 'strip_gain',
- STRIP_PAN: 'strip_pan',
- STRIP_MUTE: 'strip_mute',
- STRIP_PLUGIN_DESC: 'strip_plugin_desc',
- STRIP_PLUGIN_ENABLE: 'strip_plugin_enable',
- STRIP_PLUGIN_PARAM_DESC: 'strip_plugin_param_desc',
+ TEMPO: 'tempo',
+ STRIP_DESC: 'strip_desc',
+ STRIP_METER: 'strip_meter',
+ STRIP_GAIN: 'strip_gain',
+ STRIP_PAN: 'strip_pan',
+ STRIP_MUTE: 'strip_mute',
+ STRIP_PLUGIN_DESC: 'strip_plugin_desc',
+ STRIP_PLUGIN_ENABLE: 'strip_plugin_enable',
+ STRIP_PLUGIN_PARAM_DESC: 'strip_plugin_param_desc',
STRIP_PLUGIN_PARAM_VALUE: 'strip_plugin_param_value'
});
@@ -49,7 +49,7 @@ export class Message {
}
}
- static hash (node, addr) {
+ static nodeAddrId (node, addr) {
return [node].concat(addr || []).join('_');
}
@@ -74,8 +74,8 @@ export class Message {
return JSON.stringify({node: this.node, addr: this.addr, val: val});
}
- get hash () {
- return Message.hash(this.node, this.addr);
+ get nodeAddrId () {
+ return Message.nodeAddrId(this.node, this.addr);
}
toString () {