pbc/libs/modep.js

401 lines
14 KiB
JavaScript

const util = require('./util.js');
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 osc = require('./osc.js');
const path = require('path');
const fs = require('fs');
const ttl2jsonld = require('@frogcat/ttl2jsonld').parse;
let pedalboardIdBypassOrigin;
async function reset() {
await util.httpGET(global.config.modep.host, global.config.modep.port, '/reset');
}
async function getBanks() {
let banks = cache.getBanks();
if (banks !== undefined) {
return banks;
}
banks = await util.httpGET(global.config.modep.host, global.config.modep.port, '/banks');
for (let index = 0; index < banks.length; index++) {
let bank = banks[index];
bank.id = index;
}
banks = util.sortById(banks);
cache.setBanks(banks);
return banks;
}
async function getBankById(bankId) {
const banks = await getBanks();
for (let index = 0; index < banks.length; index++) {
if (banks[index].id != bankId) {
continue;
}
return banks[index];
}
throw new Error('could not find bank by id \'' + bankId + '\'');
}
async function getPedalboards() {
let pedalboards = cache.getPedalboards();
if (pedalboards !== undefined) {
return pedalboards;
}
pedalboards = await util.httpGET(global.config.modep.host, global.config.modep.port, '/pedalboard/list');
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;
continue;
}
pedalboard.id = id;
id++;
}
pedalboards = util.sortById(pedalboards);
cache.setPedalboards(pedalboards);
return pedalboards;
}
async function getDefaultPedalboard() {
let defaultPedalboard = cache.getDefaultPedalboard();
if (defaultPedalboard !== undefined) {
return defaultPedalboard;
}
defaultPedalboard = await getPedalboardByBundle(constants.PEDALBOARD_DEFAULT);
cache.setDefaultPedalboard(defaultPedalboard);
return defaultPedalboard;
}
async function getCurrentPedalboard() {
let currentPedalboard = cache.getCurrentPedalboard();
if (currentPedalboard !== undefined) {
return currentPedalboard;
}
currentPedalboard = await getPedalboardByBundle(await util.httpGET(global.config.modep.host, global.config.modep.port, '/pedalboard/current'));
cache.setCurrentPedalboard(currentPedalboard);
return currentPedalboard;
}
async function getPedalboardById(pedalboardId) {
const pedalboards = await getPedalboards();
if (pedalboards === undefined) {
return;
}
for (let index = 0; index < pedalboards.length; index++) {
if (pedalboards[index].id != pedalboardId) {
continue;
}
return pedalboards[index];
}
throw new Error('could not find pedalboard by id \'' + pedalboardId + '\'');
}
async function getPedalboardByBundle(pedalboardBundle) {
if (pedalboardBundle === undefined) {
return await getDefaultPedalboard();
}
const pedalboards = await getPedalboards();
for (let index = 0; index < pedalboards.length; index++) {
if (pedalboards[index].bundle != pedalboardBundle) {
continue;
}
return pedalboards[index];
}
throw new Error('could not find pedalboard by bundle \'' + pedalboardBundle + '\'');
}
async function getPedalControlById(pedalId, controlId) {
const pedal = await getCurrentPedalById(pedalId);
for (let index = 0; index < pedal.controls.length; index++) {
if (pedal.controls[index].id != controlId) {
continue;
}
return pedal.controls[index];
}
throw new Error('could not find control for pedal \'' + pedalId + '\' by id \'' + controlId + '\'');
}
async function getCurrentPedalById(id) {
const currentPedals = await getCurrentPedals();
for (let index = 0; index < currentPedals.length; index++) {
if (currentPedals[index].id != id) {
continue;
}
return currentPedals[index];
}
throw new Error('could not find current pedal by id \'' + id + '\'');
}
async function getCurrentPedals() {
let currentPedals = cache.getCurrentPedals();
if (currentPedals !== undefined) {
return currentPedals;
}
return await parseCurrentPedalboard(await getCurrentPedalboard());
}
async function parseCurrentPedalboard(currentPedalboard) {
let pedals = [];
const defaultPedalboard = await getDefaultPedalboard();
if (defaultPedalboard.id === currentPedalboard.id) {
cache.setCurrentPedals(pedals);
blinky.setStatus(currentPedalboard, pedals);
return pedals;
}
let startTime = new Date();
logger.debug('parsing current pedalboard \'' + currentPedalboard.title + '\'...');
if (currentPedalboard.uri === undefined) {
throw new Error('could not determine current pedalboard config file');
}
let file = path.resolve(currentPedalboard.uri.replace('file://', ''));
if (global.config?.dev) {
file = path.resolve(path.dirname(__dirname) + '/dev/' + currentPedalboard.uri.substring(currentPedalboard.uri.lastIndexOf('/') + 1));
}
const data = await new Promise((resolve, reject) => {
fs.readFile(file, (err, data) => {
if (err) {
return reject('could not parse current pedalboard file \'' + file + '\' >>> ' + err);
}
resolve(data);
});
});
let json = ttl2jsonld(data.toString())['@graph'];
let id = 0;
for (let index = 0; index < json.length; index++) {
let tmp = json[index];
if (tmp['lv2:prototype'] === undefined) {
continue;
}
let name = tmp['@id'];
pedals.push({ id: id, name: name, controls: [] });
id++;
}
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;
}
let pedal = undefined;
for (let pedalIndex = 0; pedalIndex < pedals.length; pedalIndex++) {
if (!name.startsWith(pedals[pedalIndex].name)) {
continue;
}
pedal = pedals[pedalIndex];
break;
}
if (pedal === undefined) {
continue;
}
id = pedal.controls.length;
name = name.replace(pedal.name + '/', '');
if (name !== constants.CONTROL_BYPASS) {
value = value['@value'];
}
let control = { id, name, value };
pedal.controls.push(control);
id++;
let midi = tmp['midi:binding'];
if (midi === undefined) {
continue;
}
control.midi = { channel: midi['midi:channel'], controller: midi['midi:controllerNumber'] }
}
logger.debug('parsing current pedalboard file \'' + file + '\' took ' + util.timeDiff(startTime) + 'ms')
cache.setCurrentPedals(pedals);
blinky.setStatus(currentPedalboard, pedals);
return pedals;
}
async function setControlByRequest(pedalId, requestParams) {
if (requestParams === undefined) {
throw new Error('could not handle POST missing all parameters', 400);
}
let controlId = requestParams.get('id');
if (controlId === undefined || controlId === null) {
throw new Error('could not handle POST - missing parameter \'id\'', 400);
}
if (isNaN(controlId)) {
throw new Error('parameter \'id\' is not a number', 400);
}
let value = requestParams.get('value');
if (value === undefined || value === null) {
throw new Error('could not handle POST - missing parameter \'value\'', 400);
}
if (isNaN(value)) {
throw new Error('parameter \'value\' is not a number', 400);
}
return await setControl(await getPedalControlById(pedalId, controlId), parseInt(value));
}
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');
}
control.midi.value = await osc.send(control.midi.controller, control.midi.channel, value);
cache.updateControl(control);
logger.info('set value for control \'' + control.name + '\' to \'' + value + '\'');
}
async function setPedalboardById(pedalboardId) {
if (pedalboardId === undefined) {
throw new Error('no pedalboard id given');
}
if (isNaN(pedalboardId)) {
throw new Error('given pedalboard id \'' + pedalboardId + '\' is not a number');
}
return await setPedalboard(await getPedalboardById(pedalboardId));
}
async function setPedalboard(pedalboard) {
if (pedalboard?.bundle === undefined) {
throw new Error('no bundle set for pedalboard \'' + pedalboard.title + '\'');
}
if (pedalboard.id === await getCurrentPedalboard().id) {
throw new Error('pedalboard with id \'' + currentPedalboard.id + '\' is already active');
}
await reset();
await util.httpPOST(global.config.modep.host, global.config.modep.port, '/pedalboard/load_bundle/?bundlepath=' + pedalboard.bundle);
cache.setCurrentPedalboard(pedalboard);
logger.info('set current pedalboard to \'' + pedalboard.title + '\'');
return await parseCurrentPedalboard(pedalboard);
}
function hasControlMidiBindings(control) {
return control?.midi !== undefined;
}
function isBypassActive(control) {
return (control.midi?.value === undefined && control.value >= 1) || control.midi?.value === 0;
}
function isPedalBypassed(pedal) {
return isBypassActive(getBypassControlFromPedal(pedal));
}
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) {
const currentPedalboard = await getCurrentPedalboard();
const 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);
getPedalboardById
return { status: 'ok', pedalboard: await getPedalboardById(pedalboardId) };
} catch (err) {
throw new Error(err);
}
}
const pedal = await getCurrentPedalById(pedalId);
const bypass = getBypassControlFromPedal(pedal);
let value = 0;
if (isBypassActive(bypass)) {
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);
}
logger.info('toggled bypass for pedal \'' + pedal.name + '\'');
return { status: 'ok', bypass: isBypassActive(bypass), control: bypass };
}
async function saveCurrentPedalboard(id, args) {
const currentPedalboard = await getCurrentPedalboard();
const defaultPedalboard = await getDefaultPedalboard();
if (currentPedalboard.id === defaultPedalboard.id) {
throw new Error('not saving default pedalboard \'' + defaultPedalboard.title + '\'');
}
let title = args?.get('title') || currentPedalboard.title;
let newBoard = 1;
if (title === currentPedalboard.title) {
newBoard = 0;
} else {
const pedalboards = await getPedalboards();
for (let index = 0; index < pedalboards.length; index++) {
if (pedalboards[index].title === title) {
throw new Error('not saving current state of pedalboard \'' + currentPedalboard.title + '\' as new pedalboard \'' + title + '\', pedalboard already exists')
}
}
}
await util.httpPOST(global.config.modep.host, global.config.modep.port, '/pedalboard/save', { title: title, asNew: newBoard });
result = {
status: 'ok',
title: title,
pedalboard: currentPedalboard
};
if (newBoard === 0) {
logger.info('saved current state of pedalboard \'' + currentPedalboard.title + '\'');
} else {
logger.info('saved current state of pedalboard \'' + currentPedalboard.title + '\' as new pedalboard \'' + title + '\'');
cache.clearPedalboards();
await getPedalboards();
}
return {
status: 'ok',
title: title,
pedalboard: currentPedalboard
};
}
module.exports = {
getBanks,
getBankById,
getPedalboards,
getPedalboardById,
getDefaultPedalboard,
getCurrentPedalboard,
saveCurrentPedalboard,
getCurrentPedals,
getCurrentPedalById,
getPedalControlById,
setPedalboard,
setPedalboardById,
setControlByRequest,
toggleBypass,
isBypassActive,
isPedalBypassed,
getBypassControlFromPedal,
hasControlMidiBindings
}