fixed a lot of stuff, implemented blinky functionality

This commit is contained in:
Daniel Sommer 2022-03-08 00:51:58 +01:00
parent c5869151a8
commit 99be4a2ad5
10 changed files with 376 additions and 228 deletions

View file

@ -1,10 +1,10 @@
{ {
"server": { "server": {
"listen": "0.0.0.0", "listen": "0.0.0.0",
"port": 3000 "port": 3001
}, },
"cache": { "cache": {
"active": false, "active": true,
"lifetime": 15 "lifetime": 15
}, },
"log": { "log": {
@ -22,22 +22,12 @@
}, },
"blinky": { "blinky": {
"enabled": true, "enabled": true,
"host": "192.168.1.31", "host": "192.168.1.70",
"port": 3000, "port": 3000,
"events": { "colors": {
"started": { "BigMuffPi": "#065535",
"mode": "set", "Overdrive": "#ffcc55",
"args": { "StompBox_fuzz": "#C767B0"
"blinkstick": "square",
"color": "#0f0f0f"
}
},
"stopped": {
"mode": "poweroff",
"args": {
"blinkstick": "square"
}
}
} }
} }
} }

View file

@ -66,6 +66,11 @@ function setupEndpoints() {
constants.API_PEDALBOARDS_CURRENT, constants.API_PEDALBOARDS_CURRENT,
{ method: modep.getCurrentPedalboard } { method: modep.getCurrentPedalboard }
)) ))
.then(setEndpoint(
constants.API_BYPASS,
undefined,
{ method: modep.toggleBypass }
))
.then(modep.getCurrentPedals) .then(modep.getCurrentPedals)
.then(function (pedals) { .then(function (pedals) {
setEndpoint( setEndpoint(
@ -77,7 +82,12 @@ function setupEndpoints() {
setEndpoint( setEndpoint(
constants.API_PEDALS + '/' + id, constants.API_PEDALS + '/' + id,
{ method: modep.getCurrentPedalById, id: id }, { method: modep.getCurrentPedalById, id: id },
{ method: modep.setControlValue, id: id } { method: modep.setControlByRequest, id: id }
);
setEndpoint(
constants.API_BYPASS + '/' + id,
undefined,
{ method: modep.toggleBypass, id: id }
); );
} }
}) })

View file

@ -1,4 +1,5 @@
const logger = require('./logger.js'); const logger = require('./logger.js');
const { CONTROL_BYPASS } = require('./constants.js');
const { httpPOST } = require('./util.js'); const { httpPOST } = require('./util.js');
let enabled; let enabled;
@ -7,28 +8,115 @@ async function initialize() {
enabled = global.config.blinky !== undefined && enabled = global.config.blinky !== undefined &&
global.config.blinky.enabled && global.config.blinky.enabled &&
global.config.blinky.host !== undefined && global.config.blinky.host !== undefined &&
global.config.blinky.port !== undefined && global.config.blinky.port !== undefined;
global.config.blinky.events !== undefined;
} }
async function handleEvent(eventName) { async function setActive(active) {
if (!enabled || eventName === undefined) { if (!enabled) {
return; return;
} }
const event = global.config.blinky.events[eventName]; logger.info('handling blinky event \'setActive\' (argument: \'' + active + '\')...');
if (event === undefined || event.mode === undefined) {
return;
}
logger.info('handling blinky event \'' + eventName + '\'...');
try { try {
await httpPOST(global.config.blinky.host, global.config.blinky.port, event.mode, event.args); if (active) {
logger.debug('blinky event \'' + eventName + '\' successfully handled'); await httpPOST(
global.config.blinky.host,
global.config.blinky.port,
'/set',
{ blinkstick: 'square', color: '#0f0f0f' }
);
return;
}
let mode = '/poweroff';
await httpPOST(
global.config.blinky.host,
global.config.blinky.port,
mode,
{ blinkstick: 'square' }
);
await httpPOST(
global.config.blinky.host,
global.config.blinky.port,
mode,
{ blinkstick: 'strip' }
);
} catch (err) { } catch (err) {
logger.error('blinky event \'' + eventName + '\' encountered an error: ' + err); logger.error(err);
} }
} }
async function setPedalStatus(pedal) {
if (!enabled || pedal === undefined || pedal.controls === undefined) {
return;
}
for (let index = 0; index < pedal.controls.length; index++) {
const bypass = pedal.controls[index];
if (bypass.name !== CONTROL_BYPASS) {
continue;
}
const args = {
blinkstick: "strip",
index: pedal.id
};
let mode = 'poweroff';
if (bypass.value !== undefined && bypass.value > 0) {
mode = 'set';
args.color = getPedalColorByName(pedal.name);
}
return httpPOST(global.config.blinky.host, global.config.blinky.port, mode, args);
}
}
async function setStatus(pedals) {
if (!enabled) {
return;
}
try {
await httpPOST(global.config.blinky.host, global.config.blinky.port, '/poweroff', { blinkstick: 'strip' });
if (pedals === undefined || pedals.length === 0) {
return;
}
const promises = [];
for (let index = 0; index < pedals.length; index++) {
if (pedals[index]?.controls === undefined || pedals[index]?.controls.length === 0) {
continue;
}
promises.push(setPedalStatus(pedals[index]));
}
const results = await Promise.allSettled(promises);
for (let index = 0; index < results.length; index++) {
const result = results[index];
if (result.status !== 'fulfilled') {
logger.error(result.reason);
}
}
} catch (err) {
logger.error(err);
}
}
async function setBypass(bypassActive) {
if (!enabled) {
return;
}
try {
if (!bypassActive) {
return await setActive(true);
}
await httpPOST(global.config.blinky.host, global.config.blinky.port, 'pulse', { blinkstick: 'square', color: '#660000' });
} catch (err) {
logger.error(err);
}
}
function getPedalColorByName(name) {
return global.config.blinky?.colors[name] || 'random';
}
module.exports = { module.exports = {
initialize, initialize,
handleEvent setActive,
setBypass,
setStatus,
setPedalStatus
} }

View file

@ -20,18 +20,18 @@ function clear() {
} }
function validLifetime(timestamp) { function validLifetime(timestamp) {
if (timestamp == undefined) { if (timestamp === undefined) {
return false; return false;
} }
if ((new Date().getTime() - timestamp) >= lifetime) { if (lifetime === 0) {
return false; return true;
} }
return true; return (new Date().getTime() - timestamp) <= lifetime;
} }
function getValue(key) { function getValue(key) {
var value = cache.get(key); var value = cache.get(key);
if (value == undefined) { if (value === undefined) {
return undefined; return undefined;
} }
if (!validLifetime(value.timestamp)) { if (!validLifetime(value.timestamp)) {

View file

@ -15,7 +15,7 @@ function execute(cmd, args) {
resolve(); resolve();
}); });
spawned.on('error', function (err) { spawned.on('error', function (err) {
return reject('error: command \'' + cmd + '\' with args \'' + args + '\' encountered an error >>> ' + err); return reject('command \'' + cmd + '\' with args \'' + args + '\' encountered an error >>> ' + err);
}); });
}); });
} }

View file

@ -8,6 +8,7 @@ exports.API_PEDALBOARDS = '/pedalboards';
exports.API_PEDALBOARDS_DEFAULT = '/pedalboards/default'; exports.API_PEDALBOARDS_DEFAULT = '/pedalboards/default';
exports.API_PEDALBOARDS_CURRENT = '/pedalboards/current'; exports.API_PEDALBOARDS_CURRENT = '/pedalboards/current';
exports.API_PEDALS = '/pedals'; exports.API_PEDALS = '/pedals';
exports.API_BYPASS = '/bypass';
exports.CACHE_INFO = "info"; exports.CACHE_INFO = "info";
exports.CACHE_BANKS = "banks"; exports.CACHE_BANKS = "banks";
@ -17,3 +18,5 @@ exports.CACHE_PEDALBOARD_CURRENT = "pedalboard_current";
exports.CACHE_PEDALS = "pedals"; exports.CACHE_PEDALS = "pedals";
exports.PEDALBOARD_DEFAULT = '/var/modep/pedalboards/default.pedalboard'; exports.PEDALBOARD_DEFAULT = '/var/modep/pedalboards/default.pedalboard';
exports.CONTROL_BYPASS = ':bypass';

View file

@ -4,10 +4,13 @@ const constants = require('./constants.js');
const logger = require('./logger.js'); const logger = require('./logger.js');
const cache = require('./cache.js'); const cache = require('./cache.js');
const commands = require('./commands.js'); const commands = require('./commands.js');
const blinky = require('./blinky.js');
const path = require('path'); const path = require('path');
const fs = require('fs'); const fs = require('fs');
const ttl2jsonld = require('@frogcat/ttl2jsonld').parse; const ttl2jsonld = require('@frogcat/ttl2jsonld').parse;
let pedalboardIdBypassOrigin;
function reset() { function reset() {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
util.httpGET(config.modep.host, config.modep.port, '/reset') util.httpGET(config.modep.host, config.modep.port, '/reset')
@ -18,14 +21,14 @@ function reset() {
function getBanks() { function getBanks() {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
var banks = cache.getBanks(); let banks = cache.getBanks();
if (banks != undefined) { if (banks != undefined) {
return resolve(banks); return resolve(banks);
} }
// FAKE DATA // FAKE DATA
// var fake = [{ "title": "The Button", "pedalboards": [{ "valid": true, "broken": false, "uri": "file:///var/modep/pedalboards/default.pedalboard/default.ttl", "bundle": "/var/modep/pedalboards/default.pedalboard", "title": "Default", "version": 0 }, { "valid": true, "broken": false, "uri": "file:///var/modep/pedalboards/FUZZ.pedalboard/FUZZ.ttl", "bundle": "/var/modep/pedalboards/FUZZ.pedalboard", "title": "FUZZ", "version": 1 }] }]; // let fake = [{ "title": "The Button", "pedalboards": [{ "valid": true, "broken": false, "uri": "file:///var/modep/pedalboards/default.pedalboard/default.ttl", "bundle": "/var/modep/pedalboards/default.pedalboard", "title": "Default", "version": 0 }, { "valid": true, "broken": false, "uri": "file:///var/modep/pedalboards/FUZZ.pedalboard/FUZZ.ttl", "bundle": "/var/modep/pedalboards/FUZZ.pedalboard", "title": "FUZZ", "version": 1 }] }];
// for (var index = 0; index < fake.length; index++) { // for (let index = 0; index < fake.length; index++) {
// fake.id = index; // fake.id = index;
// } // }
// banks = util.sortById(fake); // banks = util.sortById(fake);
@ -33,8 +36,8 @@ function getBanks() {
util.httpGET(config.modep.host, config.modep.port, '/banks') util.httpGET(config.modep.host, config.modep.port, '/banks')
.then(function (banks) { .then(function (banks) {
for (var index = 0; index < banks.length; index++) { for (let index = 0; index < banks.length; index++) {
var bank = banks[index]; let bank = banks[index];
bank.id = index; bank.id = index;
} }
banks = util.sortById(banks); banks = util.sortById(banks);
@ -49,13 +52,13 @@ function getBankById(bankId) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
getBanks() getBanks()
.then(function (banks) { .then(function (banks) {
for (var index = 0; index < banks.length; index++) { for (let index = 0; index < banks.length; index++) {
if (banks[index].id != bankId) { if (banks[index].id != bankId) {
continue; continue;
} }
return resolve(banks[index]); return resolve(banks[index]);
} }
return reject('error: could not find bank by id \'' + bankId + '\''); return reject('could not find bank by id \'' + bankId + '\'');
}) })
.catch(reject); .catch(reject);
}); });
@ -63,15 +66,15 @@ function getBankById(bankId) {
function getPedalboards() { function getPedalboards() {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
var pedalboards = cache.getPedalboards(); let pedalboards = cache.getPedalboards();
if (pedalboards != undefined) { if (pedalboards != undefined) {
return resolve(pedalboards); return resolve(pedalboards);
} }
// FAKE DATA // FAKE DATA
// var fake = [{ "valid": true, "broken": false, "uri": "file:///var/modep/pedalboards/FUZZ.pedalboard/FUZZ.ttl", "bundle": "/var/modep/pedalboards/FUZZ.pedalboard", "title": "FUZZ", "version": 1 }, { "valid": true, "broken": false, "uri": "file:///var/modep/pedalboards/default.pedalboard/default.ttl", "bundle": "/var/modep/pedalboards/default.pedalboard", "title": "Default", "version": 0 }]; // let fake = [{ "valid": true, "broken": false, "uri": "file:///var/modep/pedalboards/FUZZ.pedalboard/FUZZ.ttl", "bundle": "/var/modep/pedalboards/FUZZ.pedalboard", "title": "FUZZ", "version": 1 }, { "valid": true, "broken": false, "uri": "file:///var/modep/pedalboards/default.pedalboard/default.ttl", "bundle": "/var/modep/pedalboards/default.pedalboard", "title": "Default", "version": 0 }];
// var id = 1; // let id = 1;
// for (var index = 0; index < fake.length; index++) { // for (let index = 0; index < fake.length; index++) {
// var pedalboard = fake[index]; // let pedalboard = fake[index];
// if (pedalboard.bundle == constants.PEDALBOARD_DEFAULT) { // if (pedalboard.bundle == constants.PEDALBOARD_DEFAULT) {
// pedalboard.id = 0; // pedalboard.id = 0;
// defaultPedalboard = pedalboard; // defaultPedalboard = pedalboard;
@ -85,9 +88,9 @@ function getPedalboards() {
util.httpGET(config.modep.host, config.modep.port, '/pedalboard/list') util.httpGET(config.modep.host, config.modep.port, '/pedalboard/list')
.then(function (pedalboards) { .then(function (pedalboards) {
var id = 1; let id = 1;
for (var index = 0; index < pedalboards.length; index++) { for (let index = 0; index < pedalboards.length; index++) {
var pedalboard = pedalboards[index]; let pedalboard = pedalboards[index];
if (pedalboard.bundle == constants.PEDALBOARD_DEFAULT) { if (pedalboard.bundle == constants.PEDALBOARD_DEFAULT) {
pedalboard.id = 0; pedalboard.id = 0;
defaultPedalboard = pedalboard; defaultPedalboard = pedalboard;
@ -106,7 +109,7 @@ function getPedalboards() {
function getDefaultPedalboard() { function getDefaultPedalboard() {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
var defaultPedalboard = cache.getDefaultPedalboard(); let defaultPedalboard = cache.getDefaultPedalboard();
if (defaultPedalboard != undefined) { if (defaultPedalboard != undefined) {
return resolve(defaultPedalboard); return resolve(defaultPedalboard);
} }
@ -121,12 +124,12 @@ function getDefaultPedalboard() {
function getCurrentPedalboard() { function getCurrentPedalboard() {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
var currentPedalboard = cache.getCurrentPedalboard(); let currentPedalboard = cache.getCurrentPedalboard();
if (currentPedalboard != undefined && currentPedalboard.id != undefined) { if (currentPedalboard != undefined && currentPedalboard.id != undefined) {
return resolve(currentPedalboard); return resolve(currentPedalboard);
} }
// FAKE DATA // FAKE DATA
// var fake = '/var/modep/pedalboards/FUZZ.pedalboard'; // let fake = '/var/modep/pedalboards/FUZZ.pedalboard';
// getPedalboardByBundle(fake) // getPedalboardByBundle(fake)
// .then(function (pedalboard) { // .then(function (pedalboard) {
// currentPedalboard = pedalboard; // currentPedalboard = pedalboard;
@ -149,13 +152,13 @@ function getPedalboardById(pedalboardId) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
getPedalboards() getPedalboards()
.then(function (pedalboards) { .then(function (pedalboards) {
for (var index = 0; index < pedalboards.length; index++) { for (let index = 0; index < pedalboards.length; index++) {
if (pedalboards[index].id != pedalboardId) { if (pedalboards[index].id != pedalboardId) {
continue; continue;
} }
return resolve(pedalboards[index]); return resolve(pedalboards[index]);
} }
return reject('error: could not find pedalboard by id \'' + pedalboardId + '\''); return reject('could not find pedalboard by id \'' + pedalboardId + '\'');
}) })
.catch(reject); .catch(reject);
}); });
@ -171,13 +174,13 @@ function getPedalboardByBundle(pedalboardBundle) {
} }
getPedalboards() getPedalboards()
.then(function (pedalboards) { .then(function (pedalboards) {
for (var index = 0; index < pedalboards.length; index++) { for (let index = 0; index < pedalboards.length; index++) {
if (pedalboards[index].bundle != pedalboardBundle) { if (pedalboards[index].bundle != pedalboardBundle) {
continue; continue;
} }
return resolve(pedalboards[index]); return resolve(pedalboards[index]);
} }
return reject('error: could not find pedalboard by bundle \'' + pedalboardBundle + '\''); return reject('could not find pedalboard by bundle \'' + pedalboardBundle + '\'');
}) })
.catch(reject); .catch(reject);
}); });
@ -187,13 +190,13 @@ function getPedalControlById(pedalId, controlId) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
getCurrentPedalById(pedalId) getCurrentPedalById(pedalId)
.then(function (pedal) { .then(function (pedal) {
for (var index = 0; index < pedal.controls.length; index++) { for (let index = 0; index < pedal.controls.length; index++) {
if (pedal.controls[index].id != controlId) { if (pedal.controls[index].id != controlId) {
continue; continue;
} }
return resolve(pedal.controls[index]); return resolve(pedal.controls[index]);
} }
return reject('error: could not find control for pedal \'' + pedalId + '\' by id \'' + controlId + '\''); return reject('could not find control for pedal \'' + pedalId + '\' by id \'' + controlId + '\'');
}) })
.catch(reject); .catch(reject);
}); });
@ -203,13 +206,13 @@ function getCurrentPedalById(id) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
getCurrentPedals() getCurrentPedals()
.then(function (currentPedals) { .then(function (currentPedals) {
for (var index = 0; index < currentPedals.length; index++) { for (let index = 0; index < currentPedals.length; index++) {
if (currentPedals[index].id != id) { if (currentPedals[index].id != id) {
continue; continue;
} }
return resolve(currentPedals[index]); return resolve(currentPedals[index]);
} }
return reject('error: could not find current pedal by id \'' + id + '\''); return reject('could not find current pedal by id \'' + id + '\'');
}) })
.catch(reject); .catch(reject);
}); });
@ -217,128 +220,137 @@ function getCurrentPedalById(id) {
function getCurrentPedals() { function getCurrentPedals() {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
var currentPedals = cache.getCurrentPedals(); let currentPedals = cache.getCurrentPedals();
if (currentPedals) { if (currentPedals) {
return resolve(currentPedals); return resolve(currentPedals);
} }
getCurrentPedalboard() getCurrentPedalboard()
.then(parseCurrentPedalboard) .then(parseCurrentPedalboard)
.then(function (pedals) { .then(resolve)
cache.setCurrentPedals(pedals);
return resolve(pedals);
})
.catch(reject); .catch(reject);
}); });
} }
function parseCurrentPedalboard(currentPedalboard) { async function parseCurrentPedalboard(currentPedalboard) {
return new Promise(function (resolve, reject) { let pedals = [];
getDefaultPedalboard() const defaultPedalboard = await getDefaultPedalboard();
.then(function (defaultPedalboard) { if (defaultPedalboard.id !== currentPedalboard.id) {
test = defaultPedalboard; let startTime = new Date();
currentPedals = []; logger.debug('parsing current pedalboard...');
if (defaultPedalboard.id == currentPedalboard.id) { if (currentPedalboard.uri === undefined) {
return resolve(currentPedals); throw new Error('could not determine current pedalboard config file');
}
// FAKE DATA
let file = path.resolve(path.dirname(__dirname) + '/dev/' + currentPedalboard.uri.substring(currentPedalboard.uri.lastIndexOf('/') + 1));
// let file = path.resolve(currentPedalboard.uri.replace('file://', ''));
pedals = await new Promise((resolve, reject) => {
fs.readFile(file, function (err, data) {
if (err) {
return reject('could not parse current pedalboard file \'' + file + '\' >>> ' + err);
} }
var startTime = new Date(); let json = ttl2jsonld(data.toString())['@graph'];
logger.debug('parsing current pedalboard...'); let id = 0;
if (!currentPedalboard.uri) { let currentPedals = [];
reject('error: could not determine current pedalboard config file'); for (let index = 0; index < json.length; index++) {
let tmp = json[index];
if (!tmp['lv2:prototype']) {
continue;
}
let name = tmp['@id'];
currentPedals.push({ id: id, name: name, controls: [] });
id++;
} }
// FAKE DATA for (let index = 0; index < json.length; index++) {
var file = path.resolve(path.dirname(__dirname) + '/dev/' + currentPedalboard.uri.substring(currentPedalboard.uri.lastIndexOf('/') + 1)); let tmp = json[index];
// var file = path.resolve(currentPedalboard.uri.replace('file://', '')); let name = tmp['@id'];
fs.readFile(file, function (err, data) { let value = tmp['ingen:value'];
if (err) { if (value == undefined) {
return reject('error: could not parse current pedalboard file \'' + file + '\' >>> ' + err); continue;
} }
var json = ttl2jsonld(data.toString())['@graph']; let pedal = undefined;
var id = 0; for (let pedalIndex = 0; pedalIndex < currentPedals.length; pedalIndex++) {
for (var index = 0; index < json.length; index++) { if (!name.startsWith(currentPedals[pedalIndex].name)) {
var tmp = json[index];
if (!tmp['lv2:prototype']) {
continue; continue;
} }
var name = tmp['@id']; pedal = currentPedals[pedalIndex];
currentPedals.push({ id: id, name: name, controls: [] }); break;
id++;
} }
for (var index = 0; index < json.length; index++) { if (!pedal) {
var tmp = json[index]; continue;
var name = tmp['@id'];
var value = tmp['ingen:value'];
if (value == undefined) {
continue;
}
var pedal = undefined;
for (var pedalIndex = 0; pedalIndex < currentPedals.length; pedalIndex++) {
if (!name.startsWith(currentPedals[pedalIndex].name)) {
continue;
}
pedal = currentPedals[pedalIndex];
break;
}
if (!pedal) {
continue;
}
id = pedal.controls.length;
name = name.replace(pedal.name + '/', '');
if (name == ':bypass') {
value = value;
} else {
value = value['@value'];
}
var control = { id: id, name: name, value: value };
pedal.controls.push(control);
id++;
var midi = tmp['midi:binding'];
if (!midi) {
continue;
}
control.midi = { channel: midi['midi:channel'], controller: midi['midi:controllerNumber'] }
} }
logger.debug('parsing current pedalboard file \'' + file + '\' took ' + util.timeDiff(startTime) + 'ms') id = pedal.controls.length;
resolve(currentPedals); name = name.replace(pedal.name + '/', '');
}); if (name == constants.CONTROL_BYPASS) {
}).catch(reject); value = value;
}); } else {
value = value['@value'];
}
let control = { id: id, name: name, value: value };
pedal.controls.push(control);
id++;
let midi = tmp['midi:binding'];
if (!midi) {
continue;
}
control.midi = { channel: midi['midi:channel'], controller: midi['midi:controllerNumber'] }
}
logger.debug('parsing current pedalboard file \'' + file + '\' took ' + util.timeDiff(startTime) + 'ms')
resolve(currentPedals);
});
});
}
cache.setCurrentPedals(pedals);
blinky.setStatus(pedals);
return pedals;
} }
function setControlValue(pedalId, requestParams) { function setControlByRequest(pedalId, requestParams) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
var controlId = requestParams.get('id'); let controlId = requestParams.get('id');
if (controlId == undefined) { if (controlId == undefined) {
reject('error: could not handle POST - missing parameter \'id\'', 400); reject('could not handle POST - missing parameter \'id\'', 400);
} }
var value = parseInt(requestParams.get('value')); let value = parseInt(requestParams.get('value'));
if (value == undefined) { if (value == undefined) {
reject('error: could not handle POST - missing parameter \'value\'', 400); reject('could not handle POST - missing parameter \'value\'', 400);
} else if (Number.isNaN(value)) { } else if (Number.isNaN(value)) {
reject('error: parameter \'value\' is not a number', 400); reject('parameter \'value\' is not a number', 400);
} }
getPedalControlById(pedalId, controlId) getPedalControlById(pedalId, controlId)
.then(function (control) { .then(function (control) {
if (!control || !control.midi) { resolve(setControl(control, value));
return reject('error: control \'' + control.name + '\' with id \'' + control.id + '\' has no midi bindings');
}
if (value > 127) {
value = 127;
} else if (value < 0) {
value = 0;
}
value = '00' + util.toHex(value) + '0' + util.toHex(control.midi.controller) + 'b' + util.toHex(control.midi.channel);
var cmd = 'oscsend';
var args = [config.osc.host, config.osc.port, config.osc.address, 'm', value];
commands.execute(cmd, args);
}) })
.catch(reject); .catch(reject);
}); });
} }
async function setControl(control, value) {
if (control === undefined || control.midi === undefined) {
throw new Error('control \'' + control.name + '\' with id \'' + control.id + '\' has no midi bindings');
} if (value === undefined || isNaN(value) || value < 0) {
value = 0;
} else if (value > 127) {
value = 127;
}
let cmd = 'oscsend';
let args = [
config.osc.host, config.osc.port,
config.osc.address,
'm',
'00' + util.toHex(value) + '0' + util.toHex(control.midi.controller) + 'b' + util.toHex(control.midi.channel)
];
try {
await commands.execute(cmd, args);
control.value = value;
} catch (err) {
throw new Error(err);
}
}
function setPedalboardById(pedalboardId) { function setPedalboardById(pedalboardId) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
if (pedalboardId == undefined) { if (pedalboardId == undefined) {
return reject('error: no pedalboard id given'); return reject('no pedalboard id given');
} }
getPedalboardById(pedalboardId) getPedalboardById(pedalboardId)
.then(setPedalboard) .then(setPedalboard)
@ -350,15 +362,13 @@ function setPedalboardById(pedalboardId) {
function setPedalboard(pedalboard) { function setPedalboard(pedalboard) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
if (!pedalboard || !pedalboard.bundle) { if (!pedalboard || !pedalboard.bundle) {
return reject('error: no bundle set for pedalboard'); return reject('no bundle set for pedalboard');
} }
var tmpPedalboard;
getCurrentPedalboard() getCurrentPedalboard()
.then(function (currentPedalboard) { .then(function (currentPedalboard) {
if (pedalboard.id == currentPedalboard.id) { if (pedalboard.id === currentPedalboard.id) {
return Promise.reject('pedalboard \'' + pedalboard.id + '\' is already active'); return Promise.reject('pedalboard with id \'' + currentPedalboard.id + '\' is already active');
} }
tmpPedalboard = currentPedalboard;
}) })
.then(function () { .then(function () {
return reset() return reset()
@ -367,13 +377,85 @@ function setPedalboard(pedalboard) {
return util.httpPOST(config.modep.host, config.modep.port, '/pedalboard/load_bundle/?bundlepath=' + pedalboard.bundle) return util.httpPOST(config.modep.host, config.modep.port, '/pedalboard/load_bundle/?bundlepath=' + pedalboard.bundle)
}) })
.then(function () { .then(function () {
parseCurrentPedalboard(tmpPedalboard); cache.setCurrentPedalboard(pedalboard);
return parseCurrentPedalboard(pedalboard);
}) })
.then(resolve) .then(resolve)
.catch(reject); .catch(reject);
}); });
} }
function hasControlMidiBindings(control) {
return control !== undefined && control.midi !== undefined;
}
function isBypassed(control) {
return control.name === constants.CONTROL_BYPASS && control
}
function isPedalBypassed(pedal) {
const control = getBypassControlFromPedal(pedal);
return control.value === undefined || control.value === 0;
}
function getBypassControlFromPedal(pedal) {
if (pedal === undefined) {
throw new Error('could not get bypass for an undefined pedal');
}
if (pedal.controls === undefined || pedal.controls.length === 0) {
throw new Error('could not get bypass for pedal \'' + pedal.name + '\' with id \'' + pedal.id + '\', pedal has no controls');
}
let bypass;
for (let index = 0; index < pedal.controls.length; index++) {
const control = pedal.controls[index];
if (control.name === constants.CONTROL_BYPASS) {
bypass = pedal.controls[index];
break;
}
}
if (bypass === undefined) {
throw new Error('could not find bypass control for pedal \'' + pedal.name + '\' with id \'' + pedal.id + '\'');
}
return bypass;
}
async function toggleBypass(pedalId) {
let currentPedalboard = await getCurrentPedalboard();
let defaultPedalboard = await getDefaultPedalboard();
if (currentPedalboard.id === defaultPedalboard.id && pedalboardIdBypassOrigin === undefined) {
throw new Error('could not activate bypass, default pedalboard is currently already active');
}
if (pedalId === undefined) {
try {
let pedalboardId;
if (pedalboardIdBypassOrigin === undefined) {
pedalboardId = defaultPedalboard.id;
pedalboardIdBypassOrigin = currentPedalboard.id;
} else {
pedalboardId = pedalboardIdBypassOrigin;
pedalboardIdBypassOrigin = undefined;
}
await setPedalboardById(pedalboardId);
blinky.setBypass(pedalboardId === defaultPedalboard.id);
} catch (err) {
throw new Error(err);
}
return;
}
const pedal = await getCurrentPedalById(pedalId);
const bypass = getBypassControlFromPedal(pedal);
let value = 0;
if (bypass.value === undefined || bypass.value === 0) {
value = 127;
}
try {
await setControl(bypass, value);
blinky.setPedalStatus([pedal]);
} catch (err) {
throw new Error('could not toggle bypass for pedal ' + pedal.name + ' with id \'' + pedal.id + '\' > ' + err);
}
}
module.exports = { module.exports = {
getBanks, getBanks,
getBankById, getBankById,
@ -386,5 +468,10 @@ module.exports = {
getPedalControlById, getPedalControlById,
setPedalboard, setPedalboard,
setPedalboardById, setPedalboardById,
setControlValue, setControlByRequest,
toggleBypass,
isPedalBypassed,
hasControlMidiBindings,
isPedalBypassed,
getBypassControlFromPedal
} }

View file

@ -1,7 +1,6 @@
const config = require('../config.json'); const config = require('../config.json');
const logger = require('./logger.js'); const logger = require('./logger.js');
const api = require('./api.js') const api = require('./api.js');
const modep = require('./modep.js');
const blinky = require('./blinky.js'); const blinky = require('./blinky.js');
const http = require('http'); const http = require('http');
@ -13,7 +12,7 @@ async function start() {
} }
server.listen(config.server.port, config.server.listen).on('listening', function () { server.listen(config.server.port, config.server.listen).on('listening', function () {
logger.debug('server listening on ' + config.server.listen + ':' + config.server.port + '...'); logger.debug('server listening on ' + config.server.listen + ':' + config.server.port + '...');
blinky.handleEvent('started'); blinky.setActive(true);
handleRequests(); handleRequests();
}); });
} }
@ -39,7 +38,7 @@ function handleRequests() {
endRequest( endRequest(
request, request,
response, response,
'error: endpoint \'' + request.url + '\' does not have any handlers for ' + request.method + ' requests', 'endpoint \'' + request.url + '\' does not have any handlers for ' + request.method + ' requests',
405 405
); );
return; return;
@ -69,10 +68,10 @@ function endRequest(request, response, msg, code) {
code: code code: code
} }
if (msg != undefined) { if (msg != undefined) {
if (msg instanceof Object) { if (msg instanceof Error) {
object.data = JSON.stringify(msg);
} else if (msg instanceof Error) {
object.data = msg.toString(); object.data = msg.toString();
} else if (msg instanceof Object) {
object.data = JSON.stringify(msg);
} else { } else {
object.data = msg; object.data = msg;
} }
@ -99,38 +98,6 @@ function getRequestParams(request) {
}); });
} }
function handlePOSTPedals(request) {
return new Promise(function (resolve, reject) {
getRequestParams(request)
.then(function (params) {
var pedalId = request.url.substring(request.url.lastIndexOf('/') + 1);
var controlId = params.get('id');
if (controlId == undefined) {
reject('error: could not handle POST - missing parameter \'id\'', 400)
}
var value = params.get('value');
if (value == undefined) {
reject('error: could not handle POST - missing parameter \'value\'', 400)
}
modep.getPedalControlById(pedalId, controlId)
.then(function (control) {
modep.sendValueToControl(value, control);
})
.then(resolve)
.catch(reject);
})
.catch(reject);
});
}
function handlePOSTPedalboards(request) {
return new Promise(function (resolve, reject) {
modep.setPedalboardById(request.url.substring(request.url.lastIndexOf('/') + 1))
.then(resolve)
.catch(reject);
});
}
module.exports = { module.exports = {
start start
} }

View file

@ -2,7 +2,6 @@ const logger = require('./logger.js');
const realpath = require('fs/promises').realpath; const realpath = require('fs/promises').realpath;
const stat = require('fs/promises').stat; const stat = require('fs/promises').stat;
const http = require('http'); const http = require('http');
const querystring = require('querystring');
const { HTTP_GET, HTTP_POST } = require('./constants.js'); const { HTTP_GET, HTTP_POST } = require('./constants.js');
function timeDiff(startTime) { function timeDiff(startTime) {
@ -39,23 +38,26 @@ function httpRequest(host, port, path, method, args) {
path: path, path: path,
method: method method: method
}; };
let requestName = 'http \'' + method + '\' request > \'' + host + ':' + port + path;
if (args !== undefined) { if (args !== undefined) {
args = new URLSearchParams(args).toString(); args = new URLSearchParams(args).toString();
options.headers = { options.headers = {
'Content-Type': 'application/x-www-form-urlencoded' 'Content-Type': 'application/x-www-form-urlencoded'
} }
requestName += '?' + args;
} }
logger.debug('sending http \'' + method + '\' request to \'' + host + ':' + port + path + '\'...'); requestName += '\'';
logger.debug('sending ' + requestName + '...');
const request = http.request(options, function (response) { const request = http.request(options, function (response) {
if (!response) { if (!response) {
return reject('error: no response from host for http \'' + method + '\' request \'' + host + ':' + port + path + '\''); return reject('no response from host for ' + requestName);
} }
var responseData = ""; var responseData = "";
response.on('data', function (data) { response.on('data', function (data) {
responseData += data; responseData += data;
}); });
response.on('end', function () { response.on('end', function () {
logger.debug('http \'' + method + '\' request \'' + host + ':' + port + path + '\' returned status code \'' + response.statusCode + '\' and data \'' + responseData + '\''); logger.debug(requestName + ' returned status code \'' + response.statusCode + '\' and data \'' + responseData + '\'');
var fn = resolve; var fn = resolve;
if (response.statusCode != 200) { if (response.statusCode != 200) {
fn = reject; fn = reject;
@ -72,7 +74,7 @@ function httpRequest(host, port, path, method, args) {
}); });
}); });
request.on('error', function (err) { request.on('error', function (err) {
return reject('http \'' + method + '\' request \'' + host + ':' + port + path + '\' returned an error >>> ' + err.message); return reject(requestName + ' returned an error >>> ' + err.message);
}); });
if (args !== undefined) { if (args !== undefined) {
request.write(args); request.write(args);
@ -121,21 +123,7 @@ function resolvePath(file) {
}); });
} }
function exit(err, code) {
if (code === undefined) {
code = 0;
if (err !== undefined) {
code = 1;
}
}
if (err) {
logger.error(err);
logger.error(global.appName + ' ' + global.appVersion + ' ended due to an error');
} else {
logger.info(global.appName + ' ' + global.appVersion + ' shutting down gracefully')
}
process.exit(code);
}
module.exports = { module.exports = {
timeDiff, timeDiff,
@ -145,6 +133,5 @@ module.exports = {
toHex, toHex,
sortById, sortById,
fileExists, fileExists,
resolvePath, resolvePath
exit
} }

30
pbc.js
View file

@ -5,13 +5,13 @@ const util = require('./libs/util.js');
const blinky = require('./libs/blinky.js'); const blinky = require('./libs/blinky.js');
const packageJSON = require('./package.json'); const packageJSON = require('./package.json');
const INTERRUPTS = ['SIGINT', 'SIGTERM']; const INTERRUPTS = ['beforeExit', 'SIGINT', 'SIGTERM'];
global.appName = packageJSON.name; global.appName = packageJSON.name;
global.appVersion = packageJSON.version; global.appVersion = packageJSON.version;
global.config = process.argv[2] || __dirname + '/config.json'; global.config = process.argv[2] || __dirname + '/config.json';
handleInterrupts(); handleExit();
util.fileExists(config) util.fileExists(config)
.then((result) => { .then((result) => {
@ -26,14 +26,30 @@ util.fileExists(config)
.then(api.setupEndpoints) .then(api.setupEndpoints)
.then(server.start) .then(server.start)
.catch((err) => { .catch((err) => {
util.exit(err); exit(err);
}); });
function handleInterrupts() { function handleExit() {
for (var index = 0; index < INTERRUPTS.length; index++) { for (var index = 0; index < INTERRUPTS.length; index++) {
process.once(INTERRUPTS[index], async (code) => { process.on(INTERRUPTS[index], async (code) => {
await blinky.handleEvent('stopped'); exit(undefined, code);
util.exit(undefined, code);
}); });
} }
} }
async function exit(err, code) {
await blinky.setActive(false);
if (code === undefined) {
code = 0;
if (err !== undefined) {
code = 1;
}
}
if (err) {
logger.error(err);
logger.error(global.appName + ' ' + global.appVersion + ' ended due to an error');
} else {
logger.info(global.appName + ' ' + global.appVersion + ' shutting down gracefully')
}
process.exit(code);
}