mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-16 19:56:31 +01:00
WebSockets: improve JS client and demo
add methods to callback.js automatically reconnect js client on disconnection mixer-demo do not recreate UI on reconnection NO-OP: indentation in message.js make client JS reconnection optional fix mixer-demo scrolling minor JS client refactor improve mixer-demo readability
This commit is contained in:
parent
612c71aa25
commit
50ba8dea96
9 changed files with 191 additions and 145 deletions
|
|
@ -29,13 +29,13 @@ using namespace ARDOUR;
|
||||||
#define NODE_METHOD_PAIR(x) (Node::x, &WebsocketsDispatcher::x##_handler)
|
#define NODE_METHOD_PAIR(x) (Node::x, &WebsocketsDispatcher::x##_handler)
|
||||||
|
|
||||||
WebsocketsDispatcher::NodeMethodMap
|
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 (tempo)
|
||||||
NODE_METHOD_PAIR (strip_gain)
|
NODE_METHOD_PAIR (strip_gain)
|
||||||
NODE_METHOD_PAIR (strip_pan)
|
NODE_METHOD_PAIR (strip_pan)
|
||||||
NODE_METHOD_PAIR (strip_mute)
|
NODE_METHOD_PAIR (strip_mute)
|
||||||
NODE_METHOD_PAIR (strip_plugin_enable)
|
NODE_METHOD_PAIR (strip_plugin_enable)
|
||||||
NODE_METHOD_PAIR (strip_plugin_param_value);
|
NODE_METHOD_PAIR (strip_plugin_param_value);
|
||||||
|
|
||||||
void
|
void
|
||||||
WebsocketsDispatcher::dispatch (Client client, const NodeStateMessage& msg)
|
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 (std::string ("i"));
|
||||||
val.push_back (pd.lower);
|
val.push_back (pd.lower);
|
||||||
val.push_back (pd.upper);
|
val.push_back (pd.upper);
|
||||||
val.push_back (pd.integer_step);
|
|
||||||
} else {
|
} else {
|
||||||
val.push_back (std::string ("d"));
|
val.push_back (std::string ("d"));
|
||||||
val.push_back (pd.lower);
|
val.push_back (pd.lower);
|
||||||
|
|
|
||||||
|
|
@ -92,10 +92,10 @@ WebsocketsServer::WebsocketsServer (ArdourSurface::ArdourWebsockets& surface)
|
||||||
#if LWS_LIBRARY_VERSION_MAJOR < 3
|
#if LWS_LIBRARY_VERSION_MAJOR < 3
|
||||||
/* older libwebsockets does not define mime type for svg files */
|
/* older libwebsockets does not define mime type for svg files */
|
||||||
memset (&_lws_vhost_opt, 0, sizeof (lws_protocol_vhost_options));
|
memset (&_lws_vhost_opt, 0, sizeof (lws_protocol_vhost_options));
|
||||||
_lws_vhost_opt.name = ".svg";
|
_lws_vhost_opt.name = ".svg";
|
||||||
_lws_vhost_opt.value = "image/svg+xml";
|
_lws_vhost_opt.value = "image/svg+xml";
|
||||||
_lws_mnt_index.extra_mimetypes = &_lws_vhost_opt;
|
_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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ div {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
box-shadow: 0px 0px 10px #000;
|
box-shadow: 0px 0px 10px #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,6 +77,10 @@ div {
|
||||||
color: rgb(172,128,255);
|
color: rgb(172,128,255);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
color: rgb(99,208,230);
|
||||||
|
}
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
color: rgb(249,36,114);
|
color: rgb(249,36,114);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// This example does not call the API methods in ardour.js,
|
// This example does not call the API methods in ardour.js,
|
||||||
// instead it interacts at a lower level by coupling the widgets
|
// instead it couples the widgets directly to the message stream
|
||||||
// tightly to the message stream
|
|
||||||
|
|
||||||
import { ANode, Message } from '/shared/message.js';
|
import { ANode, Message } from '/shared/message.js';
|
||||||
import { ArdourClient } from '/shared/ardour.js';
|
import { ArdourClient } from '/shared/ardour.js';
|
||||||
|
|
@ -29,8 +28,6 @@ import { Switch, DiscreteSlider, ContinuousSlider, LogarithmicSlider,
|
||||||
(() => {
|
(() => {
|
||||||
|
|
||||||
const MAX_LOG_LINES = 1000;
|
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 ardour = new ArdourClient(location.host);
|
||||||
const widgets = {};
|
const widgets = {};
|
||||||
|
|
@ -43,101 +40,95 @@ import { Switch, DiscreteSlider, ContinuousSlider, LogarithmicSlider,
|
||||||
div.innerHTML = `${manifest.name.toUpperCase()} v${manifest.version} — ${manifest.description}`;
|
div.innerHTML = `${manifest.name.toUpperCase()} v${manifest.version} — ${manifest.description}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
ardour.addCallback({
|
ardour.addCallbacks({
|
||||||
onMessage: (msg) => {
|
onConnected: (error) => { log('Client connected', 'info'); },
|
||||||
log(`↙ ${msg}`, 'message-in');
|
onDisconnected: (error) => { log('Client disconnected', 'error'); },
|
||||||
|
onMessage: processMessage,
|
||||||
if (msg.node == ANode.STRIP_DESC) {
|
onStripDesc: createStrip,
|
||||||
createStrip (msg.addr, ...msg.val);
|
onStripPluginDesc: createStripPlugin,
|
||||||
} else if (msg.node == ANode.STRIP_PLUGIN_DESC) {
|
onStripPluginParamDesc: createStripPluginParam
|
||||||
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.open();
|
ardour.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
function createStrip (addr, name) {
|
function createStrip (stripId, name) {
|
||||||
const id = `strip-${addr[0]}`;
|
const domId = `strip-${stripId}`;
|
||||||
|
if (document.getElementById(domId) != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const strips = document.getElementById('strips');
|
const strips = document.getElementById('strips');
|
||||||
const div = createElem(`<div class="strip" id="${id}"></div>`, strips);
|
const div = createElem(`<div class="strip" id="${domId}"></div>`, strips);
|
||||||
createElem(`<label class="comp-name" for="${id}">∿  ${name}</label>`, div);
|
createElem(`<label class="comp-name" for="${domId}">∿  ${name}</label>`, div);
|
||||||
|
|
||||||
// meter
|
// meter
|
||||||
const meter = new StripMeter(ANode.STRIP_METER, addr);
|
const meter = new StripMeter();
|
||||||
meter.el.classList.add('slider-meter');
|
meter.el.classList.add('slider-meter');
|
||||||
meter.attach(div);
|
meter.appendTo(div);
|
||||||
register(meter);
|
connectWidget(meter, ANode.STRIP_METER, stripId);
|
||||||
|
|
||||||
// gain
|
// gain
|
||||||
let holder = createElem(`<div class="strip-slider"></div>`, div);
|
let holder = createElem(`<div class="strip-slider"></div>`, div);
|
||||||
createElem(`<label>Gain</label>`, holder);
|
createElem(`<label>Gain</label>`, holder);
|
||||||
const gain = new StripGainSlider(ANode.STRIP_GAIN, addr);
|
const gain = new StripGainSlider();
|
||||||
gain.attach(holder, (val) => send(gain));
|
gain.appendTo(holder);
|
||||||
register(gain);
|
connectWidget(gain, ANode.STRIP_GAIN, stripId);
|
||||||
|
|
||||||
// pan
|
// pan
|
||||||
holder = createElem(`<div class="strip-slider"></div>`, div);
|
holder = createElem(`<div class="strip-slider"></div>`, div);
|
||||||
createElem(`<label>Pan</label>`, holder);
|
createElem(`<label>Pan</label>`, holder);
|
||||||
const pan = new StripPanSlider(ANode.STRIP_PAN, addr);
|
const pan = new StripPanSlider();
|
||||||
pan.attach(holder, (val) => send(pan));
|
pan.appendTo(holder);
|
||||||
register(pan);
|
connectWidget(pan, ANode.STRIP_PAN, stripId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createStripPlugin (addr, name) {
|
function createStripPlugin (stripId, pluginId, name) {
|
||||||
const strip = document.getElementById(`strip-${addr[0]}`);
|
const domId = `plugin-${stripId}-${pluginId}`;
|
||||||
const id = `plugin-${addr[0]}-${addr[1]}`;
|
if (document.getElementById(domId) != null) {
|
||||||
const div = createElem(`<div class="plugin" id="${id}"></div>`, strip);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const strip = document.getElementById(`strip-${stripId}`);
|
||||||
|
const div = createElem(`<div class="plugin" id="${domId}"></div>`, strip);
|
||||||
createElem(`<label class="comp-name">⨍  ${name}</label>`, div);
|
createElem(`<label class="comp-name">⨍  ${name}</label>`, div);
|
||||||
const enable = new Switch(ANode.STRIP_PLUGIN_ENABLE, addr);
|
|
||||||
|
const enable = new Switch();
|
||||||
enable.el.classList.add('plugin-enable');
|
enable.el.classList.add('plugin-enable');
|
||||||
enable.attach(div, (val) => send(enable));
|
enable.appendTo(div);
|
||||||
register(enable);
|
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;
|
let param, cssClass;
|
||||||
|
|
||||||
if (dataType == 'b') {
|
if (valueType == 'b') {
|
||||||
cssClass = 'boolean';
|
cssClass = 'boolean';
|
||||||
param = new Switch(ANode.STRIP_PLUGIN_PARAM_VALUE, addr);
|
param = new Switch();
|
||||||
} else if (dataType == 'i') {
|
} else if (valueType == 'i') {
|
||||||
cssClass = 'discrete';
|
cssClass = 'discrete';
|
||||||
param = new DiscreteSlider(ANode.STRIP_PLUGIN_PARAM_VALUE, addr, min, max);
|
param = new DiscreteSlider(min, max);
|
||||||
} else if (dataType == 'd') {
|
} else if (valueType == 'd') {
|
||||||
cssClass = 'continuous';
|
cssClass = 'continuous';
|
||||||
if (isLog) {
|
if (isLog) {
|
||||||
param = new LogarithmicSlider(ANode.STRIP_PLUGIN_PARAM_VALUE, addr, min, max);
|
param = new LogarithmicSlider(min, max);
|
||||||
} else {
|
} 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 plugin = document.getElementById(`plugin-${stripId}-${pluginId}`);
|
||||||
const id = `param-${addr[0]}-${addr[1]}-${addr[2]}`;
|
const div = createElem(`<div class="plugin-param ${cssClass}" id="${domId}"></div>`, plugin);
|
||||||
const div = createElem(`<div class="plugin-param ${cssClass}" id="${id}"></div>`, plugin);
|
createElem(`<label for="${domId}">${name}</label>`, div);
|
||||||
createElem(`<label for="${id}">${name}</label>`, div);
|
|
||||||
|
|
||||||
param.attach(div, (val) => send(param));
|
param.el.name = domId;
|
||||||
param.el.name = id;
|
param.appendTo(div);
|
||||||
register(param);
|
connectWidget(param, ANode.STRIP_PLUGIN_PARAM_VALUE, stripId, pluginId, paramId);
|
||||||
}
|
|
||||||
|
|
||||||
function send (widget) {
|
|
||||||
const msg = new Message(widget.node, widget.addr, [widget.value]);
|
|
||||||
log(`↗ ${msg}`, 'message-out');
|
|
||||||
ardour.send(msg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createElem (html, parent) {
|
function createElem (html, parent) {
|
||||||
|
|
@ -153,8 +144,24 @@ import { Switch, DiscreteSlider, ContinuousSlider, LogarithmicSlider,
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
function register (widget) {
|
function connectWidget (widget, node, ...addr) {
|
||||||
widgets[widget.hash] = widget;
|
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) {
|
function log (message, className) {
|
||||||
|
|
|
||||||
|
|
@ -20,36 +20,26 @@ import { Message } from '/shared/message.js';
|
||||||
|
|
||||||
export class Widget {
|
export class Widget {
|
||||||
|
|
||||||
constructor (node, addr, html) {
|
constructor (html) {
|
||||||
this.node = node;
|
|
||||||
this.addr = addr;
|
|
||||||
const template = document.createElement('template');
|
const template = document.createElement('template');
|
||||||
template.innerHTML = html;
|
template.innerHTML = html;
|
||||||
this.el = template.content.firstChild;
|
this.el = template.content.firstChild;
|
||||||
}
|
}
|
||||||
|
|
||||||
attach (parent, callback) {
|
appendTo (parent) {
|
||||||
parent.appendChild(this.el);
|
parent.appendChild(this.el);
|
||||||
|
|
||||||
if (callback) {
|
|
||||||
this.callback = callback;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callback (value) {
|
callback (value) {
|
||||||
// do nothing by default
|
// do nothing by default
|
||||||
}
|
}
|
||||||
|
|
||||||
get hash () {
|
|
||||||
return Message.hash(this.node, this.addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Switch extends Widget {
|
export class Switch extends Widget {
|
||||||
|
|
||||||
constructor (node, addr) {
|
constructor () {
|
||||||
super (node, addr, `<input type="checkbox" class="widget-switch">`);
|
super (`<input type="checkbox" class="widget-switch">`);
|
||||||
this.el.addEventListener('input', (ev) => this.callback(this.value));
|
this.el.addEventListener('input', (ev) => this.callback(this.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,10 +55,10 @@ export class Switch extends Widget {
|
||||||
|
|
||||||
export class Slider 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"
|
const html = `<input type="range" class="widget-slider"
|
||||||
min="${min}" max="${max}" step="${step}">`;
|
min="${min}" max="${max}" step="${step}">`;
|
||||||
super(node, addr, html);
|
super(html);
|
||||||
this.min = min;
|
this.min = min;
|
||||||
this.max = max;
|
this.max = max;
|
||||||
this.el.addEventListener('input', (ev) => this.callback(this.value));
|
this.el.addEventListener('input', (ev) => this.callback(this.value));
|
||||||
|
|
@ -86,24 +76,24 @@ export class Slider extends Widget {
|
||||||
|
|
||||||
export class DiscreteSlider extends Slider {
|
export class DiscreteSlider extends Slider {
|
||||||
|
|
||||||
constructor (node, addr, min, max) {
|
constructor (min, max, step) {
|
||||||
super(node, addr, min, max, 1);
|
super(min, max, step || 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ContinuousSlider extends Slider {
|
export class ContinuousSlider extends Slider {
|
||||||
|
|
||||||
constructor (node, addr, min, max) {
|
constructor (min, max) {
|
||||||
super(node, addr, min, max, 0.001);
|
super(min, max, 0.001);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LogarithmicSlider extends ContinuousSlider {
|
export class LogarithmicSlider extends ContinuousSlider {
|
||||||
|
|
||||||
constructor (node, addr, min, max) {
|
constructor (min, max) {
|
||||||
super(node, addr, 0, 1.0);
|
super(0, 1.0);
|
||||||
this.minVal = Math.log(min);
|
this.minVal = Math.log(min);
|
||||||
this.maxVal = Math.log(max);
|
this.maxVal = Math.log(max);
|
||||||
this.scale = this.maxVal - this.minVal;
|
this.scale = this.maxVal - this.minVal;
|
||||||
|
|
@ -121,16 +111,16 @@ export class LogarithmicSlider extends ContinuousSlider {
|
||||||
|
|
||||||
export class StripPanSlider extends ContinuousSlider {
|
export class StripPanSlider extends ContinuousSlider {
|
||||||
|
|
||||||
constructor (node, addr) {
|
constructor () {
|
||||||
super(node, addr, -1.0, 1.0);
|
super(-1.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StripGainSlider extends ContinuousSlider {
|
export class StripGainSlider extends ContinuousSlider {
|
||||||
|
|
||||||
constructor (node, addr) {
|
constructor () {
|
||||||
super(node, addr, 0, 1.0)
|
super(0, 1.0)
|
||||||
this.minVal = -58.0;
|
this.minVal = -58.0;
|
||||||
this.maxVal = 6.0;
|
this.maxVal = 6.0;
|
||||||
this.scale = (this.maxVal - this.minVal);
|
this.scale = (this.maxVal - this.minVal);
|
||||||
|
|
@ -148,8 +138,8 @@ export class StripGainSlider extends ContinuousSlider {
|
||||||
|
|
||||||
export class StripMeter extends Widget {
|
export class StripMeter extends Widget {
|
||||||
|
|
||||||
constructor (node, addr) {
|
constructor () {
|
||||||
super(node, addr, `<label></label>`);
|
super(`<label></label>`);
|
||||||
}
|
}
|
||||||
|
|
||||||
set value (val) {
|
set value (val) {
|
||||||
|
|
|
||||||
|
|
@ -16,17 +16,19 @@
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { MetadataMixin } from './metadata.js';
|
|
||||||
import { ControlMixin } from './control.js';
|
import { ControlMixin } from './control.js';
|
||||||
|
import { MetadataMixin } from './metadata.js';
|
||||||
import { Message } from './message.js';
|
import { Message } from './message.js';
|
||||||
import { MessageChannel } from './channel.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 {
|
class BaseArdourClient {
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
this._callbacks = [];
|
this._callbacks = [];
|
||||||
|
this._connected = false;
|
||||||
this._pendingRequest = null;
|
this._pendingRequest = null;
|
||||||
this._channel = new MessageChannel(location.host);
|
this._channel = new MessageChannel(location.host);
|
||||||
|
|
||||||
|
|
@ -39,21 +41,30 @@ class BaseArdourClient {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
addCallback (callback) {
|
addCallbacks (callbacks) {
|
||||||
this._callbacks.push(callback);
|
this._callbacks.push(callbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
async open () {
|
async connect (autoReconnect) {
|
||||||
this._channel.onClose = () => {
|
this._channel.onClose = async () => {
|
||||||
this._fireCallbacks('error', new Error('Message channel unexpectedly closed'));
|
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.onClose = () => {};
|
||||||
this._channel.close();
|
this._channel.close();
|
||||||
|
this._connected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
send (msg) {
|
send (msg) {
|
||||||
|
|
@ -61,6 +72,12 @@ class BaseArdourClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private methods
|
// Private methods
|
||||||
|
|
||||||
|
async _connect () {
|
||||||
|
await this._channel.open();
|
||||||
|
this._connected = true;
|
||||||
|
this._fireCallbacks('connected');
|
||||||
|
}
|
||||||
|
|
||||||
_send (node, addr, val) {
|
_send (node, addr, val) {
|
||||||
const msg = new Message(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) {
|
_onChannelMessage (msg) {
|
||||||
if (this._pendingRequest && (this._pendingRequest.hash == msg.hash)) {
|
if (this._pendingRequest && (this._pendingRequest.hash == msg.hash)) {
|
||||||
this._pendingRequest.resolve(msg.val);
|
this._pendingRequest.resolve(msg.val);
|
||||||
|
|
@ -91,9 +112,9 @@ class BaseArdourClient {
|
||||||
return s[0].toUpperCase() + s.slice(1).toLowerCase();
|
return s[0].toUpperCase() + s.slice(1).toLowerCase();
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
for (const callback of this._callbacks) {
|
for (const callbacks of this._callbacks) {
|
||||||
if (method in callback) {
|
if (method in callbacks) {
|
||||||
callback[method](...args)
|
callbacks[method](...args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -102,15 +123,19 @@ class BaseArdourClient {
|
||||||
return new Error(`HTTP response status ${status}`);
|
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) {}
|
export class ArdourClient extends mixin(BaseArdourClient, ControlMixin, MetadataMixin) {}
|
||||||
|
|
||||||
function mixin (dstClass, ...classes) {
|
function mixin (dstClass, ...classes) {
|
||||||
for (const srcClass of classes) {
|
for (const srcClass of classes) {
|
||||||
for (const methName of Object.getOwnPropertyNames(srcClass.prototype)) {
|
for (const propName of Object.getOwnPropertyNames(srcClass.prototype)) {
|
||||||
if (methName != 'constructor') {
|
if (propName != 'constructor') {
|
||||||
dstClass.prototype[methName] = srcClass.prototype[methName];
|
dstClass.prototype[propName] = srcClass.prototype[propName];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,34 @@
|
||||||
|
|
||||||
export class ArdourCallback {
|
export class ArdourCallback {
|
||||||
|
|
||||||
onTempo (bpm) {}
|
// Connection status
|
||||||
onStripGain (stripId, db) {}
|
onConnected () {}
|
||||||
onStripPan (stripId, value) {}
|
onDisconnected () {}
|
||||||
onStripMute (stripId, value) {}
|
|
||||||
onStripPluginEnable (stripId, pluginId, value) {}
|
|
||||||
onStripPluginParamValue (stripId, pluginId, paramId, value) {}
|
|
||||||
|
|
||||||
|
// All messages and errors
|
||||||
onMessage (msg) {}
|
onMessage (msg) {}
|
||||||
onError (error) {}
|
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) {}
|
||||||
|
|
||||||
|
// Strip plugin parameters
|
||||||
|
// valueType
|
||||||
|
// 'b' : boolean
|
||||||
|
// 'i' : integer
|
||||||
|
// 'd' : double
|
||||||
|
onStripPluginParamDesc (stripId, pluginId, paramId, name, valueType, min, max, isLog) {}
|
||||||
|
onStripPluginParamValue (stripId, pluginId, paramId, value) {}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,27 +23,27 @@ import { ANode } from './message.js';
|
||||||
export class ControlMixin {
|
export class ControlMixin {
|
||||||
|
|
||||||
async getTempo () {
|
async getTempo () {
|
||||||
return (await this._sendAndReceive(ANode.TEMPO))[0];
|
return await this._sendRecvSingle(ANode.TEMPO);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getStripGain (stripId) {
|
async getStripGain (stripId) {
|
||||||
return (await this._sendAndReceive(ANode.STRIP_GAIN, [stripId]))[0];
|
return await this._sendRecvSingle(ANode.STRIP_GAIN, [stripId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getStripPan (stripId) {
|
async getStripPan (stripId) {
|
||||||
return (await this._sendAndReceive(ANode.STRIP_PAN, [stripId]))[0];
|
return await this._sendRecvSingle(ANode.STRIP_PAN, [stripId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getStripMute (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) {
|
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) {
|
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) {
|
setTempo (bpm) {
|
||||||
|
|
|
||||||
|
|
@ -19,15 +19,15 @@
|
||||||
export const JSON_INF = 1.0e+128;
|
export const JSON_INF = 1.0e+128;
|
||||||
|
|
||||||
export const ANode = Object.freeze({
|
export const ANode = Object.freeze({
|
||||||
TEMPO: 'tempo',
|
TEMPO: 'tempo',
|
||||||
STRIP_DESC: 'strip_desc',
|
STRIP_DESC: 'strip_desc',
|
||||||
STRIP_METER: 'strip_meter',
|
STRIP_METER: 'strip_meter',
|
||||||
STRIP_GAIN: 'strip_gain',
|
STRIP_GAIN: 'strip_gain',
|
||||||
STRIP_PAN: 'strip_pan',
|
STRIP_PAN: 'strip_pan',
|
||||||
STRIP_MUTE: 'strip_mute',
|
STRIP_MUTE: 'strip_mute',
|
||||||
STRIP_PLUGIN_DESC: 'strip_plugin_desc',
|
STRIP_PLUGIN_DESC: 'strip_plugin_desc',
|
||||||
STRIP_PLUGIN_ENABLE: 'strip_plugin_enable',
|
STRIP_PLUGIN_ENABLE: 'strip_plugin_enable',
|
||||||
STRIP_PLUGIN_PARAM_DESC: 'strip_plugin_param_desc',
|
STRIP_PLUGIN_PARAM_DESC: 'strip_plugin_param_desc',
|
||||||
STRIP_PLUGIN_PARAM_VALUE: 'strip_plugin_param_value'
|
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('_');
|
return [node].concat(addr || []).join('_');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,8 +74,8 @@ export class Message {
|
||||||
return JSON.stringify({node: this.node, addr: this.addr, val: val});
|
return JSON.stringify({node: this.node, addr: this.addr, val: val});
|
||||||
}
|
}
|
||||||
|
|
||||||
get hash () {
|
get nodeAddrId () {
|
||||||
return Message.hash(this.node, this.addr);
|
return Message.nodeAddrId(this.node, this.addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
toString () {
|
toString () {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue