Skip to content

Commit

Permalink
Rework state handling
Browse files Browse the repository at this point in the history
And fix some bugs
  • Loading branch information
lucianoiam committed Jul 26, 2022
1 parent 2ec8d8c commit a599733
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 74 deletions.
20 changes: 9 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,18 @@

*Control Surface Library*

This is a VST 2/3 plugin that provides a web user interface for controlling other plugins supporting MIDI learn. The user interface is primarily designed for touch devices. The plugin is self-contained and no additional software is required; just drop it into the same chain (and before) the target plugin.
This is a VST2 & VST3 plugin providing a web user interface for controlling other plugins that support MIDI learn. The user interface is primarily designed for touch devices. The plugin is self-contained and no additional software is required; just insert it into the target plugin's chain (and before the target, so it receives MIDI events) and make sure the computer and client devices are connected to the same network.

Example use case: tweak Amplitube parameters from a Wi-Fi tablet.
Example use case: tweak a virtual guitar amp parameters from a Wi-Fi tablet.

Current development status:
<img width="1706" alt="Screen Shot 2022-07-26 at 09 58 17" src="https://user-images.githubusercontent.com/930494/180954979-4089c388-fdb9-48ff-9434-b007a8b4a65f.png">

- Backend : complete
- Frontend : complete
- Documentation : null for the moment
Remotely accessing the UI is easy, with three options available:

Known bugs: VST3 not working on Ableton Live (VST2 on this DAW).
- Scan the plugin QR code available from the menu bar
- Use a Zeroconf/Bonjour app for discovery on [Android](https://play.google.com/store/apps/details?id=de.wellenvogel.bonjourbrowser) or [iOS](https://apps.apple.com/us/app/bonjour-search-for-http-web-in-wi-fi/id1097517829)
- Use a minimal dedicated app called [pisco](https://github.com/lucianoiam/pisco) (Android only)

Android users can have a look at [pisco](https://github.com/lucianoiam/pisco). iOS users can scan a QR code or use [this app](https://apps.apple.com/us/app/bonjour-search-for-http-web-in-wi-fi/id1097517829) for discovering the plugin user interface in the local network.
![IMG_1883](https://user-images.githubusercontent.com/930494/180954991-4a5f0d41-a07c-4394-a493-6f7f341ed7cf.jpg)

<img width="1706" alt="Screen Shot 2022-07-17 at 13 20 45" src="https://user-images.githubusercontent.com/930494/179395742-a3a6befa-fad6-41bf-a65f-6b341c0adc14.png">

![IMG_1818](https://user-images.githubusercontent.com/930494/176409115-e6e00ee2-612e-45f7-9796-c1eedde0214f.jpg)
Known bugs: VST3 not working on Ableton Live, use the VST2 version on this DAW instead.
2 changes: 1 addition & 1 deletion src/ConsulPlugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class ConsulPlugin : public PluginEx

uint32_t getVersion() const override
{
return d_version(0, 9, 0);
return d_version(1, 0, 0);
}

int64_t getUniqueId() const override
Expand Down
2 changes: 1 addition & 1 deletion src/ui/lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
function main() {
loadScript('lib/ui.js').then(_ => {
ConsulUI.init(Object.freeze({
productVersion : '0.9.0',
productVersion : '1.0.0',
defaultLayout : 'mixer',
controlDescriptor : [
{ name: 'Button', id: 'b', n: 16, cont: false, def: { base: 0 , ch: 1 } },
Expand Down
4 changes: 2 additions & 2 deletions src/ui/lib/modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,13 @@ class ModalDialog {
});

this.addEventListener(ok, 'input', ev => {
if (! ev.target.value) { // up
if (! ev.target.value) {
this.hide(true);
}
});

this.addEventListener(cancel, 'input', ev => {
if (! ev.target.value) { // up
if (! ev.target.value) {
this.hide(false);
}
});
Expand Down
129 changes: 70 additions & 59 deletions src/ui/lib/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class ConsulUI extends DISTRHO.UI {
this._showStatusTimer = null;
this._hideStatusTimer = null;

this._initMenuBar();
this._initMenuBarController();

if (isMobileDevice()) {
window.addEventListener('resize', _ => this._zoomUi());
Expand All @@ -49,7 +49,8 @@ class ConsulUI extends DISTRHO.UI {
}

if (this._env.dev) {
this._start(); // stateChanged() will not be called for dev
this.stateChanged('config', '{}');
this.stateChanged('ui', '{}');
}

DISTRHO.UIHelper.enableOfflineModal(this);
Expand All @@ -60,54 +61,48 @@ class ConsulUI extends DISTRHO.UI {
case 'config':
if (value) {
this._config = JSON.parse(value);
this._config.init = true;

if (Object.keys(this._config).length == 0) {
this._config['map'] = this._buildDefaultMidiMap();
this._config['layout'] = this._opt.defaultLayout;
this._saveConfig();
}

this._applyConfig();
}

break;
case 'ui':
if (value) {
this._uiState = JSON.parse(value);
this._uiState.init = true;
this._applyUiState();
}
break;
}

// Will be called every time config is updated by any client
if (this._config.init && this._uiState.init) {
this._start();
break;
}
}

messageReceived(args) {
if ((args[0] == 'control') && (args.length == 3)) {
elem(args[1]).value = args[2];
const id = args[1];
const value = args[2];
this._uiState[id] = value;
elem(id).value = value;
}
}

get _env() {
return DISTRHO.env;
}

_start() {
if (! this._config['map']) {
this._setConfigOption('map', this._buildDefaultMidiMap());
}

this._loadLayout(this._config['layout'] || this._opt.defaultLayout);
}

_setConfigOption(key, value) {
this._config[key] = value;
this.setState('config', JSON.stringify(this._config));
}

_initMenuBar() {
_initMenuBarController() {
const invertSvg = (el, val) => {
const fill = val ? '#000' : '#fff';
el.shadowRoot.querySelectorAll('path,polygon,circle').forEach(p => p.style.fill = fill);
};

elem('option-about').addEventListener('input', ev => {
if (! ev.target.value) { // up
if (! ev.target.value) {
new AboutModalDialog(this._opt.productVersion).show();
}
});
Expand All @@ -119,7 +114,7 @@ class ConsulUI extends DISTRHO.UI {
invertSvg(ev.target, false);
new LayoutModalDialog(this._activeLayoutId, newLayoutId => {
this._loadLayout(newLayoutId);
this._setConfigOption('layout', newLayoutId);
this._setConfigEntry('layout', newLayoutId);
}).show();
}
});
Expand All @@ -134,7 +129,7 @@ class ConsulUI extends DISTRHO.UI {
} else {
invertSvg(ev.target, false);
new MidiModalDialog(this._opt.controlDescriptor, this._config['map'], newMap => {
this._setConfigOption('map', newMap);
this._setConfigEntry('map', newMap);
}).show();
}
});
Expand All @@ -153,30 +148,6 @@ class ConsulUI extends DISTRHO.UI {
}
}

_zoomUi() {
// Use mixer size as the base size for all layouts
const baseWidth = 800;
const baseHeight = 540;
const main = elem('main');
const dv = window.innerHeight - baseHeight;

if (dv > 0) {
// Zoom interface to take up full window height
const scale = 1.0 + dv / baseHeight;
main.style.width = window.innerWidth / scale + 'px';
main.style.height = baseHeight + 'px';
main.style.transform = `scale(${100 * scale}%)`;
document.body.style.overflow = 'hidden';
} else {
// Viewport too small, ie. phone in portrait orientation. Since
// the UI elements have fixed size some of them could appear cropped.
main.style.width = '';
main.style.height = '';
main.style.transform = '';
document.body.style.overflow = 'scroll';
}
}

_showStatus(message, numericValue) {
const apply = () => {
this._showStatusTimer = null;
Expand Down Expand Up @@ -224,6 +195,30 @@ class ConsulUI extends DISTRHO.UI {
apply();
}

_zoomUi() {
// Use mixer size as the base size for all layouts
const baseWidth = 800;
const baseHeight = 540;
const main = elem('main');
const dv = window.innerHeight - baseHeight;

if (dv > 0) {
// Zoom interface to take up full window height
const scale = 1.0 + dv / baseHeight;
main.style.width = window.innerWidth / scale + 'px';
main.style.height = baseHeight + 'px';
main.style.transform = `scale(${100 * scale}%)`;
document.body.style.overflow = 'hidden';
} else {
// Viewport too small, ie. phone in portrait orientation. Since
// the UI elements have fixed size some of them could appear cropped.
main.style.width = '';
main.style.height = '';
main.style.transform = '';
document.body.style.overflow = 'scroll';
}
}

async _loadLayout(id) {
if (this._activeLayoutId == id) {
return;
Expand Down Expand Up @@ -252,14 +247,6 @@ class ConsulUI extends DISTRHO.UI {
el.addEventListener('input', _ => this._handleControlInput(el));
});

// Restore state
for (const controlId in this._uiState) {
const control = elem(controlId);
if (control) {
elem(controlId).value = this._uiState[controlId];
}
}

// Plugin embedded view size
if (this._env.plugin) {
const size = this._getActiveLayoutCSSSize();
Expand All @@ -272,6 +259,8 @@ class ConsulUI extends DISTRHO.UI {
this._zoomUi(); // relative to startup size (CSS #main)
}

this._applyUiState();

document.body.style.visibility = 'visible';
}

Expand Down Expand Up @@ -325,4 +314,26 @@ class ConsulUI extends DISTRHO.UI {
}
}

_applyUiState() {
for (const controlId in this._uiState) {
const control = elem(controlId);
if (control) {
elem(controlId).value = this._uiState[controlId];
}
}
}

_applyConfig() {
this._loadLayout(this._config['layout']);
}

_saveConfig() {
this.setState('config', JSON.stringify(this._config));
}

_setConfigEntry(key, value) {
this._config[key] = value;
this._saveConfig();
}

}

0 comments on commit a599733

Please sign in to comment.