diff --git a/config.json b/config.json index 05fa704..a51f34a 100644 --- a/config.json +++ b/config.json @@ -1,10 +1,10 @@ { "server": { "listen": "0.0.0.0", - "port": 3000 + "port": 3001 }, "cache": { - "active": false, + "active": true, "lifetime": 15 }, "log": { @@ -22,22 +22,12 @@ }, "blinky": { "enabled": true, - "host": "192.168.1.31", + "host": "192.168.1.70", "port": 3000, - "events": { - "started": { - "mode": "set", - "args": { - "blinkstick": "square", - "color": "#0f0f0f" - } - }, - "stopped": { - "mode": "poweroff", - "args": { - "blinkstick": "square" - } - } + "colors": { + "BigMuffPi": "#065535", + "Overdrive": "#ffcc55", + "StompBox_fuzz": "#C767B0" } } } \ No newline at end of file diff --git a/libs/api.js b/libs/api.js index 25375e3..2058b3c 100644 --- a/libs/api.js +++ b/libs/api.js @@ -22,7 +22,7 @@ function setEndpoint(url, get, post) { logger.debug('set up endpoint \'' + url + '\' > ' + JSON.stringify(endpoint)); return resolve(); }); - + } function getEndpoints() { @@ -66,6 +66,11 @@ function setupEndpoints() { constants.API_PEDALBOARDS_CURRENT, { method: modep.getCurrentPedalboard } )) + .then(setEndpoint( + constants.API_BYPASS, + undefined, + { method: modep.toggleBypass } + )) .then(modep.getCurrentPedals) .then(function (pedals) { setEndpoint( @@ -77,7 +82,12 @@ function setupEndpoints() { setEndpoint( constants.API_PEDALS + '/' + 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 } ); } }) diff --git a/libs/blinky.js b/libs/blinky.js index 5a29741..672007f 100644 --- a/libs/blinky.js +++ b/libs/blinky.js @@ -1,4 +1,5 @@ const logger = require('./logger.js'); +const { CONTROL_BYPASS } = require('./constants.js'); const { httpPOST } = require('./util.js'); let enabled; @@ -7,28 +8,115 @@ async function initialize() { enabled = global.config.blinky !== undefined && global.config.blinky.enabled && global.config.blinky.host !== undefined && - global.config.blinky.port !== undefined && - global.config.blinky.events !== undefined; + global.config.blinky.port !== undefined; } -async function handleEvent(eventName) { - if (!enabled || eventName === undefined) { +async function setActive(active) { + if (!enabled) { return; } - const event = global.config.blinky.events[eventName]; - if (event === undefined || event.mode === undefined) { - return; - } - logger.info('handling blinky event \'' + eventName + '\'...'); + logger.info('handling blinky event \'setActive\' (argument: \'' + active + '\')...'); try { - await httpPOST(global.config.blinky.host, global.config.blinky.port, event.mode, event.args); - logger.debug('blinky event \'' + eventName + '\' successfully handled'); + if (active) { + 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) { - 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 = { initialize, - handleEvent + setActive, + setBypass, + setStatus, + setPedalStatus } \ No newline at end of file diff --git a/libs/cache.js b/libs/cache.js index 2fc87b5..a9717b3 100644 --- a/libs/cache.js +++ b/libs/cache.js @@ -20,18 +20,18 @@ function clear() { } function validLifetime(timestamp) { - if (timestamp == undefined) { + if (timestamp === undefined) { return false; } - if ((new Date().getTime() - timestamp) >= lifetime) { - return false; + if (lifetime === 0) { + return true; } - return true; + return (new Date().getTime() - timestamp) <= lifetime; } function getValue(key) { var value = cache.get(key); - if (value == undefined) { + if (value === undefined) { return undefined; } if (!validLifetime(value.timestamp)) { diff --git a/libs/commands.js b/libs/commands.js index 9ee4ba4..9f27df5 100644 --- a/libs/commands.js +++ b/libs/commands.js @@ -15,7 +15,7 @@ function execute(cmd, args) { resolve(); }); 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); }); }); } diff --git a/libs/constants.js b/libs/constants.js index ec17a3e..401e28f 100644 --- a/libs/constants.js +++ b/libs/constants.js @@ -8,6 +8,7 @@ exports.API_PEDALBOARDS = '/pedalboards'; exports.API_PEDALBOARDS_DEFAULT = '/pedalboards/default'; exports.API_PEDALBOARDS_CURRENT = '/pedalboards/current'; exports.API_PEDALS = '/pedals'; +exports.API_BYPASS = '/bypass'; exports.CACHE_INFO = "info"; exports.CACHE_BANKS = "banks"; @@ -16,4 +17,6 @@ exports.CACHE_PEDALBOARD_DEFAULT = "pedalboard_default"; exports.CACHE_PEDALBOARD_CURRENT = "pedalboard_current"; exports.CACHE_PEDALS = "pedals"; -exports.PEDALBOARD_DEFAULT = '/var/modep/pedalboards/default.pedalboard'; \ No newline at end of file +exports.PEDALBOARD_DEFAULT = '/var/modep/pedalboards/default.pedalboard'; + +exports.CONTROL_BYPASS = ':bypass'; \ No newline at end of file diff --git a/libs/modep.js b/libs/modep.js index 92d2411..0a5158f 100644 --- a/libs/modep.js +++ b/libs/modep.js @@ -4,10 +4,13 @@ const constants = require('./constants.js'); const logger = require('./logger.js'); const cache = require('./cache.js'); const commands = require('./commands.js'); +const blinky = require('./blinky.js'); const path = require('path'); const fs = require('fs'); const ttl2jsonld = require('@frogcat/ttl2jsonld').parse; +let pedalboardIdBypassOrigin; + function reset() { return new Promise(function (resolve, reject) { util.httpGET(config.modep.host, config.modep.port, '/reset') @@ -18,14 +21,14 @@ function reset() { function getBanks() { return new Promise(function (resolve, reject) { - var banks = cache.getBanks(); + let banks = cache.getBanks(); if (banks != undefined) { return resolve(banks); } // 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 }] }]; - // for (var index = 0; index < fake.length; index++) { + // 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 (let index = 0; index < fake.length; index++) { // fake.id = index; // } // banks = util.sortById(fake); @@ -33,8 +36,8 @@ function getBanks() { util.httpGET(config.modep.host, config.modep.port, '/banks') .then(function (banks) { - for (var index = 0; index < banks.length; index++) { - var bank = banks[index]; + for (let index = 0; index < banks.length; index++) { + let bank = banks[index]; bank.id = index; } banks = util.sortById(banks); @@ -49,13 +52,13 @@ function getBankById(bankId) { return new Promise(function (resolve, reject) { getBanks() .then(function (banks) { - for (var index = 0; index < banks.length; index++) { + for (let index = 0; index < banks.length; index++) { if (banks[index].id != bankId) { continue; } 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); }); @@ -63,15 +66,15 @@ function getBankById(bankId) { function getPedalboards() { return new Promise(function (resolve, reject) { - var pedalboards = cache.getPedalboards(); + let pedalboards = cache.getPedalboards(); if (pedalboards != undefined) { return resolve(pedalboards); } // 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 }]; - // var id = 1; - // for (var index = 0; index < fake.length; index++) { - // var pedalboard = fake[index]; + // 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 }]; + // let id = 1; + // for (let index = 0; index < fake.length; index++) { + // let pedalboard = fake[index]; // if (pedalboard.bundle == constants.PEDALBOARD_DEFAULT) { // pedalboard.id = 0; // defaultPedalboard = pedalboard; @@ -85,9 +88,9 @@ function getPedalboards() { util.httpGET(config.modep.host, config.modep.port, '/pedalboard/list') .then(function (pedalboards) { - var id = 1; - for (var index = 0; index < pedalboards.length; index++) { - var pedalboard = pedalboards[index]; + let id = 1; + for (let index = 0; index < pedalboards.length; index++) { + let pedalboard = pedalboards[index]; if (pedalboard.bundle == constants.PEDALBOARD_DEFAULT) { pedalboard.id = 0; defaultPedalboard = pedalboard; @@ -106,7 +109,7 @@ function getPedalboards() { function getDefaultPedalboard() { return new Promise(function (resolve, reject) { - var defaultPedalboard = cache.getDefaultPedalboard(); + let defaultPedalboard = cache.getDefaultPedalboard(); if (defaultPedalboard != undefined) { return resolve(defaultPedalboard); } @@ -121,12 +124,12 @@ function getDefaultPedalboard() { function getCurrentPedalboard() { return new Promise(function (resolve, reject) { - var currentPedalboard = cache.getCurrentPedalboard(); + let currentPedalboard = cache.getCurrentPedalboard(); if (currentPedalboard != undefined && currentPedalboard.id != undefined) { return resolve(currentPedalboard); } // FAKE DATA - // var fake = '/var/modep/pedalboards/FUZZ.pedalboard'; + // let fake = '/var/modep/pedalboards/FUZZ.pedalboard'; // getPedalboardByBundle(fake) // .then(function (pedalboard) { // currentPedalboard = pedalboard; @@ -149,13 +152,13 @@ function getPedalboardById(pedalboardId) { return new Promise(function (resolve, reject) { getPedalboards() .then(function (pedalboards) { - for (var index = 0; index < pedalboards.length; index++) { + for (let index = 0; index < pedalboards.length; index++) { if (pedalboards[index].id != pedalboardId) { continue; } 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); }); @@ -171,13 +174,13 @@ function getPedalboardByBundle(pedalboardBundle) { } getPedalboards() .then(function (pedalboards) { - for (var index = 0; index < pedalboards.length; index++) { + for (let index = 0; index < pedalboards.length; index++) { if (pedalboards[index].bundle != pedalboardBundle) { continue; } 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); }); @@ -187,13 +190,13 @@ function getPedalControlById(pedalId, controlId) { return new Promise(function (resolve, reject) { getCurrentPedalById(pedalId) .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) { continue; } 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); }); @@ -203,13 +206,13 @@ function getCurrentPedalById(id) { return new Promise(function (resolve, reject) { getCurrentPedals() .then(function (currentPedals) { - for (var index = 0; index < currentPedals.length; index++) { + for (let index = 0; index < currentPedals.length; index++) { if (currentPedals[index].id != id) { continue; } 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); }); @@ -217,128 +220,137 @@ function getCurrentPedalById(id) { function getCurrentPedals() { return new Promise(function (resolve, reject) { - var currentPedals = cache.getCurrentPedals(); + let currentPedals = cache.getCurrentPedals(); if (currentPedals) { return resolve(currentPedals); } getCurrentPedalboard() .then(parseCurrentPedalboard) - .then(function (pedals) { - cache.setCurrentPedals(pedals); - return resolve(pedals); - }) + .then(resolve) .catch(reject); }); } -function parseCurrentPedalboard(currentPedalboard) { - return new Promise(function (resolve, reject) { - getDefaultPedalboard() - .then(function (defaultPedalboard) { - test = defaultPedalboard; - currentPedals = []; - if (defaultPedalboard.id == currentPedalboard.id) { - return resolve(currentPedals); +async function parseCurrentPedalboard(currentPedalboard) { + let pedals = []; + const defaultPedalboard = await getDefaultPedalboard(); + if (defaultPedalboard.id !== currentPedalboard.id) { + let startTime = new Date(); + logger.debug('parsing current pedalboard...'); + if (currentPedalboard.uri === undefined) { + 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(); - logger.debug('parsing current pedalboard...'); - if (!currentPedalboard.uri) { - reject('error: could not determine current pedalboard config file'); + let json = ttl2jsonld(data.toString())['@graph']; + let id = 0; + let currentPedals = []; + 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 - var file = path.resolve(path.dirname(__dirname) + '/dev/' + currentPedalboard.uri.substring(currentPedalboard.uri.lastIndexOf('/') + 1)); - // var file = path.resolve(currentPedalboard.uri.replace('file://', '')); - fs.readFile(file, function (err, data) { - if (err) { - return reject('error: could not parse current pedalboard file \'' + file + '\' >>> ' + err); + for (let index = 0; index < json.length; index++) { + let tmp = json[index]; + let name = tmp['@id']; + let value = tmp['ingen:value']; + if (value == undefined) { + continue; } - var json = ttl2jsonld(data.toString())['@graph']; - var id = 0; - for (var index = 0; index < json.length; index++) { - var tmp = json[index]; - if (!tmp['lv2:prototype']) { + let pedal = undefined; + for (let pedalIndex = 0; pedalIndex < currentPedals.length; pedalIndex++) { + if (!name.startsWith(currentPedals[pedalIndex].name)) { continue; } - var name = tmp['@id']; - currentPedals.push({ id: id, name: name, controls: [] }); - id++; + pedal = currentPedals[pedalIndex]; + break; } - for (var index = 0; index < json.length; index++) { - var tmp = json[index]; - 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'] } + if (!pedal) { + continue; } - logger.debug('parsing current pedalboard file \'' + file + '\' took ' + util.timeDiff(startTime) + 'ms') - resolve(currentPedals); - }); - }).catch(reject); - }); + id = pedal.controls.length; + name = name.replace(pedal.name + '/', ''); + if (name == constants.CONTROL_BYPASS) { + 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) { - var controlId = requestParams.get('id'); + let controlId = requestParams.get('id'); 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) { - reject('error: could not handle POST - missing parameter \'value\'', 400); + reject('could not handle POST - missing parameter \'value\'', 400); } 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) .then(function (control) { - if (!control || !control.midi) { - 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); + resolve(setControl(control, value)); }) .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) { return new Promise(function (resolve, reject) { if (pedalboardId == undefined) { - return reject('error: no pedalboard id given'); + return reject('no pedalboard id given'); } getPedalboardById(pedalboardId) .then(setPedalboard) @@ -350,15 +362,13 @@ function setPedalboardById(pedalboardId) { function setPedalboard(pedalboard) { return new Promise(function (resolve, reject) { if (!pedalboard || !pedalboard.bundle) { - return reject('error: no bundle set for pedalboard'); + return reject('no bundle set for pedalboard'); } - var tmpPedalboard; getCurrentPedalboard() .then(function (currentPedalboard) { - if (pedalboard.id == currentPedalboard.id) { - return Promise.reject('pedalboard \'' + pedalboard.id + '\' is already active'); + if (pedalboard.id === currentPedalboard.id) { + return Promise.reject('pedalboard with id \'' + currentPedalboard.id + '\' is already active'); } - tmpPedalboard = currentPedalboard; }) .then(function () { return reset() @@ -367,13 +377,85 @@ function setPedalboard(pedalboard) { return util.httpPOST(config.modep.host, config.modep.port, '/pedalboard/load_bundle/?bundlepath=' + pedalboard.bundle) }) .then(function () { - parseCurrentPedalboard(tmpPedalboard); + cache.setCurrentPedalboard(pedalboard); + return parseCurrentPedalboard(pedalboard); }) .then(resolve) .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 = { getBanks, getBankById, @@ -386,5 +468,10 @@ module.exports = { getPedalControlById, setPedalboard, setPedalboardById, - setControlValue, + setControlByRequest, + toggleBypass, + isPedalBypassed, + hasControlMidiBindings, + isPedalBypassed, + getBypassControlFromPedal } \ No newline at end of file diff --git a/libs/server.js b/libs/server.js index 56a1252..0ba8e2a 100644 --- a/libs/server.js +++ b/libs/server.js @@ -1,7 +1,6 @@ const config = require('../config.json'); const logger = require('./logger.js'); -const api = require('./api.js') -const modep = require('./modep.js'); +const api = require('./api.js'); const blinky = require('./blinky.js'); const http = require('http'); @@ -13,7 +12,7 @@ async function start() { } server.listen(config.server.port, config.server.listen).on('listening', function () { logger.debug('server listening on ' + config.server.listen + ':' + config.server.port + '...'); - blinky.handleEvent('started'); + blinky.setActive(true); handleRequests(); }); } @@ -39,7 +38,7 @@ function handleRequests() { endRequest( request, 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 ); return; @@ -69,10 +68,10 @@ function endRequest(request, response, msg, code) { code: code } if (msg != undefined) { - if (msg instanceof Object) { - object.data = JSON.stringify(msg); - } else if (msg instanceof Error) { + if (msg instanceof Error) { object.data = msg.toString(); + } else if (msg instanceof Object) { + object.data = JSON.stringify(msg); } else { 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 = { start } \ No newline at end of file diff --git a/libs/util.js b/libs/util.js index 374927e..5e1c6c4 100644 --- a/libs/util.js +++ b/libs/util.js @@ -2,7 +2,6 @@ const logger = require('./logger.js'); const realpath = require('fs/promises').realpath; const stat = require('fs/promises').stat; const http = require('http'); -const querystring = require('querystring'); const { HTTP_GET, HTTP_POST } = require('./constants.js'); function timeDiff(startTime) { @@ -39,23 +38,26 @@ function httpRequest(host, port, path, method, args) { path: path, method: method }; + let requestName = 'http \'' + method + '\' request > \'' + host + ':' + port + path; if (args !== undefined) { args = new URLSearchParams(args).toString(); options.headers = { '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) { 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 = ""; response.on('data', function (data) { responseData += data; }); 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; if (response.statusCode != 200) { fn = reject; @@ -72,7 +74,7 @@ function httpRequest(host, port, path, method, args) { }); }); 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) { 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 = { timeDiff, @@ -145,6 +133,5 @@ module.exports = { toHex, sortById, fileExists, - resolvePath, - exit + resolvePath } \ No newline at end of file diff --git a/pbc.js b/pbc.js index cc2dda3..f3381c9 100644 --- a/pbc.js +++ b/pbc.js @@ -5,13 +5,13 @@ const util = require('./libs/util.js'); const blinky = require('./libs/blinky.js'); const packageJSON = require('./package.json'); -const INTERRUPTS = ['SIGINT', 'SIGTERM']; +const INTERRUPTS = ['beforeExit', 'SIGINT', 'SIGTERM']; global.appName = packageJSON.name; global.appVersion = packageJSON.version; global.config = process.argv[2] || __dirname + '/config.json'; -handleInterrupts(); +handleExit(); util.fileExists(config) .then((result) => { @@ -26,14 +26,30 @@ util.fileExists(config) .then(api.setupEndpoints) .then(server.start) .catch((err) => { - util.exit(err); + exit(err); }); -function handleInterrupts() { +function handleExit() { for (var index = 0; index < INTERRUPTS.length; index++) { - process.once(INTERRUPTS[index], async (code) => { - await blinky.handleEvent('stopped'); - util.exit(undefined, code); + process.on(INTERRUPTS[index], async (code) => { + 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); } \ No newline at end of file