diff --git a/config.json b/config.json new file mode 120000 index 0000000..b350b85 --- /dev/null +++ b/config.json @@ -0,0 +1 @@ +config/config.json \ No newline at end of file diff --git a/debugConfig/config.json b/debugConfig/config.json index 1e49995..ce21cfa 100644 --- a/debugConfig/config.json +++ b/debugConfig/config.json @@ -8,7 +8,7 @@ /* whether to lock a process pages into memroy. should be true unless running on a very memory constrained system. Setting to false will cause unpredictable audio dropouts. */ - "mlock": true, + "mlock": false, /* Address on which Piddle listens for websocket connections */ "socketServerAddress": "0.0.0.0:8080", diff --git a/debugConfig/default_presets b/debugConfig/default_presets new file mode 120000 index 0000000..7f2c4c7 --- /dev/null +++ b/debugConfig/default_presets @@ -0,0 +1 @@ +../default_presets \ No newline at end of file diff --git a/default_config/AudioConfig.json b/default_config/AudioConfig.json new file mode 100644 index 0000000..2327f2e --- /dev/null +++ b/default_config/AudioConfig.json @@ -0,0 +1,12 @@ +{ + "valid": true, + "isOnboarding": false, + "isJackAudio": false, + "alsaInputDevice": "hw:3,0", + "alsaInputDeviceName": "GP-5", + "alsaOutputDevice": "hw:3,0", + "alsaOutputDeviceName": "GP-5", + "sampleRate": 44100, + "bufferSize": 256, + "numberOfBuffers": 3 +} diff --git a/default_config/currentPreset.json b/default_config/currentPreset.json new file mode 100644 index 0000000..d6359f2 --- /dev/null +++ b/default_config/currentPreset.json @@ -0,0 +1,34 @@ +{ + "modified": false, + "preset": { + "name": "Default Preset", + "input_volume_db": 0, + "output_volume_db": 0, + "items": [ + { + "instanceId": 1, + "uri": "uri://two-play/pipedal/pedalboard#Empty", + "isEnabled": true, + "controlValues": [], + "pluginName": "", + "midiBindings": [], + "midiChannelBinding": null, + "stateUpdateCount": 0, + "lv2State": [ + false, + {} + ], + "lilvPresetUri": "", + "pathProperties": {}, + "title": "", + "useModUi": false, + "iconColor": "", + "sideChainInputId": -1 + } + ], + "nextInstanceId": 1, + "snapshots": [], + "selectedSnapshot": -1, + "selectedPlugin": -1 + } +} \ No newline at end of file diff --git a/iso_codes.json b/iso_codes.json new file mode 120000 index 0000000..39cfbe0 --- /dev/null +++ b/iso_codes.json @@ -0,0 +1 @@ +config/iso_codes.json \ No newline at end of file diff --git a/packages/default_presets/presets/banks.versionInfo b/packages/default_presets/presets/banks.versionInfo new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/packages/default_presets/presets/banks.versionInfo @@ -0,0 +1 @@ +[] diff --git a/plugin_classes.json b/plugin_classes.json new file mode 120000 index 0000000..5ddac48 --- /dev/null +++ b/plugin_classes.json @@ -0,0 +1 @@ +config/plugin_classes.json \ No newline at end of file diff --git a/src/MixerEngine.cpp b/src/MixerEngine.cpp index 682143c..21d5dad 100644 --- a/src/MixerEngine.cpp +++ b/src/MixerEngine.cpp @@ -37,6 +37,11 @@ void MixerEngine::setSampleRate(uint32_t sampleRate) void MixerEngine::setMaxBufferSize(size_t frames) { maxBufferSize_ = frames; + // Allocate bus buffers for any existing buses (including the master bus + // created in the constructor, which does not have buffers yet). + for (auto& [_, bus] : buses_) { + bus->allocateBuffers(maxBufferSize_); + } } // --- Channel Management --- diff --git a/src/PiPedalSocket.cpp b/src/PiPedalSocket.cpp index a244e20..10b28d8 100644 --- a/src/PiPedalSocket.cpp +++ b/src/PiPedalSocket.cpp @@ -1234,6 +1234,7 @@ public: int channelIndex; double volume; pReader->read(&channelIndex); + pReader->consume(','); pReader->read(&volume); this->mixerApi.setChannelVolume(channelIndex, (float)volume); } @@ -1244,6 +1245,7 @@ public: int channelIndex; double pan; pReader->read(&channelIndex); + pReader->consume(','); pReader->read(&pan); this->mixerApi.setChannelPan(channelIndex, (float)pan); } @@ -1254,6 +1256,7 @@ public: int channelIndex; bool mute; pReader->read(&channelIndex); + pReader->consume(','); pReader->read(&mute); this->mixerApi.setChannelMute(channelIndex, mute); } @@ -1264,6 +1267,7 @@ public: int channelIndex; bool solo; pReader->read(&channelIndex); + pReader->consume(','); pReader->read(&solo); this->mixerApi.setChannelSolo(channelIndex, solo); } @@ -1274,6 +1278,7 @@ public: int channelIndex; std::string label; pReader->read(&channelIndex); + pReader->consume(','); pReader->read(&label); this->mixerApi.setChannelLabel(channelIndex, label); } @@ -1285,7 +1290,9 @@ public: bool enabled; double frequency; pReader->read(&channelIndex); + pReader->consume(','); pReader->read(&enabled); + pReader->consume(','); pReader->read(&frequency); this->mixerApi.setChannelHpf(channelIndex, enabled, (float)frequency); } @@ -1320,6 +1327,7 @@ public: int64_t busId; double volume; pReader->read(&busId); + pReader->consume(','); pReader->read(&volume); this->mixerApi.setBusVolume(busId, (float)volume); } @@ -1330,6 +1338,7 @@ public: int64_t busId; bool mute; pReader->read(&busId); + pReader->consume(','); pReader->read(&mute); this->mixerApi.setBusMute(busId, mute); } @@ -1341,7 +1350,9 @@ public: int64_t busId; double level; pReader->read(&channelIndex); + pReader->consume(','); pReader->read(&busId); + pReader->consume(','); pReader->read(&level); this->mixerApi.routeChannelToBus(channelIndex, busId, (float)level); } @@ -1353,7 +1364,9 @@ public: std::string name; int channels; pReader->read(&type); + pReader->consume(','); pReader->read(&name); + pReader->consume(','); pReader->read(&channels); int64_t busId = this->mixerApi.addBus(type, name, channels); this->Reply(replyTo, "mixerAddBus", busId); @@ -1373,6 +1386,7 @@ public: int64_t sceneId; std::string name; pReader->read(&sceneId); + pReader->consume(','); pReader->read(&name); std::string result = this->mixerApi.saveScene(name); this->JsonReply(replyTo, "mixerSaveScene", result.c_str()); @@ -1442,10 +1456,15 @@ public: double minValue = 0.0; double maxValue = 1.0; pReader->read(&midiChannel); + pReader->consume(','); pReader->read(&ccNumber); + pReader->consume(','); pReader->read(&targetType); + pReader->consume(','); pReader->read(&targetId); + pReader->consume(','); pReader->read(&minValue); + pReader->consume(','); pReader->read(&maxValue); this->mixerApi.addMidiMapping( midiChannel, ccNumber, targetType, targetId, @@ -1458,6 +1477,7 @@ public: int midiChannel; int ccNumber; pReader->read(&midiChannel); + pReader->consume(','); pReader->read(&ccNumber); this->mixerApi.removeMidiMapping(midiChannel, ccNumber); } @@ -1482,6 +1502,7 @@ public: std::string targetType; int64_t targetId; pReader->read(&targetType); + pReader->consume(','); pReader->read(&targetId); this->mixerApi.setMidiLearnTarget(targetType, targetId); } diff --git a/src/PiPedalSocket.cpp.bak b/src/PiPedalSocket.cpp.bak new file mode 100644 index 0000000..a244e20 --- /dev/null +++ b/src/PiPedalSocket.cpp.bak @@ -0,0 +1,3030 @@ +// Copyright (c) 2026 Robin Davies +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#include "pch.h" + +#include "Curl.hpp" +#include "PiPedalSocket.hpp" +#include "MixerApi.hpp" +#include "Updater.hpp" +#include "json.hpp" +#include "viewstream.hpp" +#include "PiPedalVersion.hpp" +#include "Tone3000Downloader.hpp" +#include +#include +#include "Lv2Log.hpp" +#include "JackConfiguration.hpp" +#include +#include +#include "Ipv6Helpers.hpp" +#include "Promise.hpp" +#include +#include "Tone3000Downloader.hpp" +#include "Tone3000Tone.hpp" + +#include "AdminClient.hpp" +#include "WifiConfigSettings.hpp" +#include "WifiDirectConfigSettings.hpp" +#include "WifiChannelSelectors.hpp" +#include "SysExec.hpp" +#include "PiPedalAlsa.hpp" +#include +#include "FileEntry.hpp" +#include "Tone3000Downloader.hpp" + +using namespace std; +using namespace pipedal; + +class CopyPresetsToBankBody +{ +public: + int64_t bankInstanceId_; + std::vector presets_; + DECLARE_JSON_MAP(CopyPresetsToBankBody); +}; +JSON_MAP_BEGIN(CopyPresetsToBankBody) +JSON_MAP_REFERENCE(CopyPresetsToBankBody, bankInstanceId) +JSON_MAP_REFERENCE(CopyPresetsToBankBody, presets) +JSON_MAP_END(); + +class WriteTone3000ReadmeBody { +public: + std::string filePath_; + tone3000::Tone tone_; + std::string thumbnailUrl_; + DECLARE_JSON_MAP(WriteTone3000ReadmeBody); +}; + +JSON_MAP_BEGIN(WriteTone3000ReadmeBody) +JSON_MAP_REFERENCE(WriteTone3000ReadmeBody, filePath) +JSON_MAP_REFERENCE(WriteTone3000ReadmeBody, tone) +JSON_MAP_REFERENCE(WriteTone3000ReadmeBody, thumbnailUrl) +JSON_MAP_END(); + +class DownloadModelsFromTone3000Body +{ +public: + std::string responseUri_; + Tone3000PkceParams tone3000PckceParams_; + std::string downloadPath_; + int64_t downloadType_ = -1; + + Tone3000DownloadType downloadType() const { return (Tone3000DownloadType)downloadType_; } + void downloadType(Tone3000DownloadType value) { downloadType_ = (int64_t)value; } + + DECLARE_JSON_MAP(DownloadModelsFromTone3000Body); +}; + +JSON_MAP_BEGIN(DownloadModelsFromTone3000Body) +JSON_MAP_REFERENCE(DownloadModelsFromTone3000Body, responseUri) +JSON_MAP_REFERENCE(DownloadModelsFromTone3000Body, tone3000PckceParams) +JSON_MAP_REFERENCE(DownloadModelsFromTone3000Body, downloadPath) +JSON_MAP_REFERENCE(DownloadModelsFromTone3000Body, downloadType) +JSON_MAP_END(); + +class ImportPresetsFromBankBody +{ +public: + int64_t bankInstanceId_; + std::vector presets_; + DECLARE_JSON_MAP(ImportPresetsFromBankBody); +}; +JSON_MAP_BEGIN(ImportPresetsFromBankBody) +JSON_MAP_REFERENCE(ImportPresetsFromBankBody, bankInstanceId) +JSON_MAP_REFERENCE(ImportPresetsFromBankBody, presets) +JSON_MAP_END() + +class RequestBankPresetsBody +{ +public: + int64_t bankInstanceId_; + DECLARE_JSON_MAP(RequestBankPresetsBody); +}; +JSON_MAP_BEGIN(RequestBankPresetsBody) +JSON_MAP_REFERENCE(RequestBankPresetsBody, bankInstanceId) +JSON_MAP_END() + +class Tone3000DownloadStartedBody +{ +public: + int64_t handle_; + std::string title_; + + DECLARE_JSON_MAP(Tone3000DownloadStartedBody); +}; + +JSON_MAP_BEGIN(Tone3000DownloadStartedBody) +JSON_MAP_REFERENCE(Tone3000DownloadStartedBody, handle) +JSON_MAP_REFERENCE(Tone3000DownloadStartedBody, title) +JSON_MAP_END() + +class Tone3000DownloadErrorBody +{ +public: + int64_t handle_; + std::string errorMessage_; + + DECLARE_JSON_MAP(Tone3000DownloadErrorBody); +}; + +JSON_MAP_BEGIN(Tone3000DownloadErrorBody) +JSON_MAP_REFERENCE(Tone3000DownloadErrorBody, handle) +JSON_MAP_REFERENCE(Tone3000DownloadErrorBody, errorMessage) +JSON_MAP_END() + +class PathPatchPropertyChangedBody +{ +public: + int64_t instanceId_; + std::string propertyUri_; + std::string atomJson_; + DECLARE_JSON_MAP(PathPatchPropertyChangedBody); +}; +JSON_MAP_BEGIN(PathPatchPropertyChangedBody) +JSON_MAP_REFERENCE(PathPatchPropertyChangedBody, instanceId) +JSON_MAP_REFERENCE(PathPatchPropertyChangedBody, propertyUri) +JSON_MAP_REFERENCE(PathPatchPropertyChangedBody, atomJson) +JSON_MAP_END() + +class GetPatchPropertyBody +{ +public: + uint64_t instanceId_; + std::string propertyUri_; + DECLARE_JSON_MAP(GetPatchPropertyBody); +}; + +JSON_MAP_BEGIN(GetPatchPropertyBody) +JSON_MAP_REFERENCE(GetPatchPropertyBody, instanceId) +JSON_MAP_REFERENCE(GetPatchPropertyBody, propertyUri) +JSON_MAP_END() + +class MoveAudioFileArgs +{ +public: + std::string path_; + int32_t from_; + int32_t to_; + DECLARE_JSON_MAP(MoveAudioFileArgs); +}; +JSON_MAP_BEGIN(MoveAudioFileArgs) +JSON_MAP_REFERENCE(MoveAudioFileArgs, path) +JSON_MAP_REFERENCE(MoveAudioFileArgs, from) +JSON_MAP_REFERENCE(MoveAudioFileArgs, to) +JSON_MAP_END() + +class CreateNewSampleDirectoryArgs +{ +public: + std::string relativePath_; + UiFileProperty uiFileProperty_; + DECLARE_JSON_MAP(CreateNewSampleDirectoryArgs); +}; + +JSON_MAP_BEGIN(CreateNewSampleDirectoryArgs) +JSON_MAP_REFERENCE(CreateNewSampleDirectoryArgs, relativePath) +JSON_MAP_REFERENCE(CreateNewSampleDirectoryArgs, uiFileProperty) +JSON_MAP_END() + +class RenameSampleFileArgs +{ +public: + std::string oldRelativePath_; + std::string newRelativePath_; + UiFileProperty uiFileProperty_; + DECLARE_JSON_MAP(RenameSampleFileArgs); +}; + +JSON_MAP_BEGIN(RenameSampleFileArgs) +JSON_MAP_REFERENCE(RenameSampleFileArgs, oldRelativePath) +JSON_MAP_REFERENCE(RenameSampleFileArgs, newRelativePath) +JSON_MAP_REFERENCE(RenameSampleFileArgs, uiFileProperty) +JSON_MAP_END() + +class CopySampleFileArgs +{ +public: + std::string oldRelativePath_; + std::string newRelativePath_; + UiFileProperty uiFileProperty_; + bool overwrite_ = false; + DECLARE_JSON_MAP(CopySampleFileArgs); +}; + +JSON_MAP_BEGIN(CopySampleFileArgs) +JSON_MAP_REFERENCE(CopySampleFileArgs, oldRelativePath) +JSON_MAP_REFERENCE(CopySampleFileArgs, newRelativePath) +JSON_MAP_REFERENCE(CopySampleFileArgs, uiFileProperty) +JSON_MAP_REFERENCE(CopySampleFileArgs, overwrite) +JSON_MAP_END() + +class GetFilePropertyDirectoryTreeArgs +{ +public: + UiFileProperty fileProperty_; + std::string selectedPath_; + + DECLARE_JSON_MAP(GetFilePropertyDirectoryTreeArgs); +}; + +JSON_MAP_BEGIN(GetFilePropertyDirectoryTreeArgs) +JSON_MAP_REFERENCE(GetFilePropertyDirectoryTreeArgs, fileProperty) +JSON_MAP_REFERENCE(GetFilePropertyDirectoryTreeArgs, selectedPath) +JSON_MAP_END(); + +class Lv2StateChangedBody +{ +public: + uint64_t instanceId_; + Lv2PluginState state_; + DECLARE_JSON_MAP(Lv2StateChangedBody); +}; + +JSON_MAP_BEGIN(Lv2StateChangedBody) +JSON_MAP_REFERENCE(Lv2StateChangedBody, instanceId) +JSON_MAP_REFERENCE(Lv2StateChangedBody, state) +JSON_MAP_END(); + +class SetPatchPropertyBody +{ +public: + uint64_t instanceId_; + std::string propertyUri_; + json_variant value_; + DECLARE_JSON_MAP(SetPatchPropertyBody); +}; + +JSON_MAP_BEGIN(SetPatchPropertyBody) +JSON_MAP_REFERENCE(SetPatchPropertyBody, instanceId) +JSON_MAP_REFERENCE(SetPatchPropertyBody, propertyUri) +JSON_MAP_REFERENCE(SetPatchPropertyBody, value) +JSON_MAP_END() + +class SetPedalboardItemTitleBody +{ +public: + uint64_t instanceId_; + std::string title_; + std::string colorKey_; + DECLARE_JSON_MAP(SetPedalboardItemTitleBody); +}; + +JSON_MAP_BEGIN(SetPedalboardItemTitleBody) +JSON_MAP_REFERENCE(SetPedalboardItemTitleBody, instanceId) +JSON_MAP_REFERENCE(SetPedalboardItemTitleBody, title) +JSON_MAP_REFERENCE(SetPedalboardItemTitleBody, colorKey) +JSON_MAP_END() + +class NotifyMidiListenerBody +{ +public: + int64_t clientHandle_; + uint16_t cc0_; + uint16_t cc1_; + uint16_t cc2_; + DECLARE_JSON_MAP(NotifyMidiListenerBody); +}; +JSON_MAP_BEGIN(NotifyMidiListenerBody) +JSON_MAP_REFERENCE(NotifyMidiListenerBody, clientHandle) +JSON_MAP_REFERENCE(NotifyMidiListenerBody, cc0) +JSON_MAP_REFERENCE(NotifyMidiListenerBody, cc1) +JSON_MAP_REFERENCE(NotifyMidiListenerBody, cc2) +JSON_MAP_END() + +class NotifyAtomOutputBody +{ +public: + int64_t clientHandle_; + uint64_t instanceId_; + std::string propertyUri_; + raw_json_string atomJson_; + + DECLARE_JSON_MAP(NotifyAtomOutputBody); +}; +JSON_MAP_BEGIN(NotifyAtomOutputBody) +JSON_MAP_REFERENCE(NotifyAtomOutputBody, clientHandle) +JSON_MAP_REFERENCE(NotifyAtomOutputBody, instanceId) +JSON_MAP_REFERENCE(NotifyAtomOutputBody, propertyUri) +JSON_MAP_REFERENCE(NotifyAtomOutputBody, atomJson) +JSON_MAP_END(); + +class ListenForMidiEventBody +{ +public: + int64_t handle_; + DECLARE_JSON_MAP(ListenForMidiEventBody); +}; + +JSON_MAP_BEGIN(ListenForMidiEventBody) +JSON_MAP_REFERENCE(ListenForMidiEventBody, handle) +JSON_MAP_END() + +class MonitorPatchPropertyBody +{ +public: + uint64_t instanceId_; + int64_t clientHandle_; + std::string propertyUri_; + DECLARE_JSON_MAP(MonitorPatchPropertyBody); +}; + +JSON_MAP_BEGIN(MonitorPatchPropertyBody) +JSON_MAP_REFERENCE(MonitorPatchPropertyBody, instanceId) +JSON_MAP_REFERENCE(MonitorPatchPropertyBody, clientHandle) +JSON_MAP_REFERENCE(MonitorPatchPropertyBody, propertyUri) +JSON_MAP_END() + +class OnLoadPluginPresetBody +{ +public: + int64_t instanceId_; + std::vector controlValues_; + DECLARE_JSON_MAP(OnLoadPluginPresetBody); +}; + +JSON_MAP_BEGIN(OnLoadPluginPresetBody) +JSON_MAP_REFERENCE(OnLoadPluginPresetBody, instanceId) +JSON_MAP_REFERENCE(OnLoadPluginPresetBody, controlValues) +JSON_MAP_END() + +class LoadPluginPresetBody +{ +public: + uint64_t pluginInstanceId_; + uint64_t presetInstanceId_; + DECLARE_JSON_MAP(LoadPluginPresetBody); +}; + +JSON_MAP_BEGIN(LoadPluginPresetBody) +JSON_MAP_REFERENCE(LoadPluginPresetBody, pluginInstanceId) +JSON_MAP_REFERENCE(LoadPluginPresetBody, presetInstanceId) +JSON_MAP_END() + +class FromToBody +{ +public: + int64_t from_ = -1; + int64_t to_ = -1; + + DECLARE_JSON_MAP(FromToBody); +}; + +JSON_MAP_BEGIN(FromToBody) +JSON_MAP_REFERENCE(FromToBody, from) +JSON_MAP_REFERENCE(FromToBody, to) +JSON_MAP_END() + +class MonitorResultBody +{ +public: + int64_t subscriptionHandle_ = -1; + float value_; + + DECLARE_JSON_MAP(MonitorResultBody); +}; +JSON_MAP_BEGIN(MonitorResultBody) +JSON_MAP_REFERENCE(MonitorResultBody, subscriptionHandle) +JSON_MAP_REFERENCE(MonitorResultBody, value) +JSON_MAP_END() + +class FileRequestArgs +{ +public: + std::string relativePath_; + UiFileProperty fileProperty_; + + DECLARE_JSON_MAP(FileRequestArgs); +}; +JSON_MAP_BEGIN(FileRequestArgs) +JSON_MAP_REFERENCE(FileRequestArgs, relativePath) +JSON_MAP_REFERENCE(FileRequestArgs, fileProperty) +JSON_MAP_END() + +class MonitorPortBody +{ +public: + int64_t instanceId_ = -1; + std::string key_; + float_t updateRate_ = 0; + + DECLARE_JSON_MAP(MonitorPortBody); +}; +JSON_MAP_BEGIN(MonitorPortBody) +JSON_MAP_REFERENCE(MonitorPortBody, instanceId) +JSON_MAP_REFERENCE(MonitorPortBody, key) +JSON_MAP_REFERENCE(MonitorPortBody, updateRate) +JSON_MAP_END() + +class SaveCurrentPresetAsBody +{ +public: + int64_t clientId_ = -1; + int64_t bankInstanceId_ = -1; + std::string name_; + int64_t saveAfterInstanceId_ = -1; + + DECLARE_JSON_MAP(SaveCurrentPresetAsBody); +}; +JSON_MAP_BEGIN(SaveCurrentPresetAsBody) +JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, clientId) +JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, bankInstanceId) +JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, name) +JSON_MAP_REFERENCE(SaveCurrentPresetAsBody, saveAfterInstanceId) +JSON_MAP_END(); + +class SavePluginPresetAsBody +{ +public: + int64_t instanceId_ = -1; + std::string name_; + + DECLARE_JSON_MAP(SavePluginPresetAsBody); +}; +JSON_MAP_BEGIN(SavePluginPresetAsBody) +JSON_MAP_REFERENCE(SavePluginPresetAsBody, instanceId) +JSON_MAP_REFERENCE(SavePluginPresetAsBody, name) +JSON_MAP_END(); + +class RenameBankBody +{ +public: + int64_t bankId_; + std::string newName_; + DECLARE_JSON_MAP(RenameBankBody); +}; +JSON_MAP_BEGIN(RenameBankBody) +JSON_MAP_REFERENCE(RenameBankBody, bankId) +JSON_MAP_REFERENCE(RenameBankBody, newName) +JSON_MAP_END(); + +class RenamePresetBody +{ +public: + int64_t clientId_ = -1; + int64_t instanceId_ = -1; + std::string name_; + + DECLARE_JSON_MAP(RenamePresetBody); +}; +JSON_MAP_BEGIN(RenamePresetBody) +JSON_MAP_REFERENCE(RenamePresetBody, clientId) +JSON_MAP_REFERENCE(RenamePresetBody, instanceId) +JSON_MAP_REFERENCE(RenamePresetBody, name) +JSON_MAP_END() + +class CopyPresetBody +{ +public: + int64_t clientId_ = -1; + int64_t fromId_ = -1; + int64_t toId_ = -1; + + DECLARE_JSON_MAP(CopyPresetBody); +}; +JSON_MAP_BEGIN(CopyPresetBody) +JSON_MAP_REFERENCE(CopyPresetBody, clientId) +JSON_MAP_REFERENCE(CopyPresetBody, fromId) +JSON_MAP_REFERENCE(CopyPresetBody, toId) +JSON_MAP_END() + +class CopyPluginPresetBody +{ +public: + std::string pluginUri_; + uint64_t instanceId_; + + DECLARE_JSON_MAP(CopyPluginPresetBody); +}; +JSON_MAP_BEGIN(CopyPluginPresetBody) +JSON_MAP_REFERENCE(CopyPluginPresetBody, pluginUri) +JSON_MAP_REFERENCE(CopyPluginPresetBody, instanceId) +JSON_MAP_END() + +class PedalboardItemEnabledBody +{ +public: + int64_t clientId_ = -1; + int64_t instanceId_ = -1; + bool enabled_ = true; + + DECLARE_JSON_MAP(PedalboardItemEnabledBody); +}; +JSON_MAP_BEGIN(PedalboardItemEnabledBody) +JSON_MAP_REFERENCE(PedalboardItemEnabledBody, clientId) +JSON_MAP_REFERENCE(PedalboardItemEnabledBody, instanceId) +JSON_MAP_REFERENCE(PedalboardItemEnabledBody, enabled) +JSON_MAP_END() + +class PedalboardItemUseModGuiBody +{ +public: + int64_t clientId_ = -1; + int64_t instanceId_ = -1; + bool useModUi_ = true; + + DECLARE_JSON_MAP(PedalboardItemUseModGuiBody); +}; +JSON_MAP_BEGIN(PedalboardItemUseModGuiBody) +JSON_MAP_REFERENCE(PedalboardItemUseModGuiBody, clientId) +JSON_MAP_REFERENCE(PedalboardItemUseModGuiBody, instanceId) +JSON_MAP_REFERENCE(PedalboardItemUseModGuiBody, useModUi) +JSON_MAP_END() + +class UpdateCurrentPedalboardBody +{ +public: + int64_t clientId_ = -1; + Pedalboard pedalboard_; + + DECLARE_JSON_MAP(UpdateCurrentPedalboardBody); +}; + +JSON_MAP_BEGIN(UpdateCurrentPedalboardBody) +JSON_MAP_REFERENCE(UpdateCurrentPedalboardBody, clientId) +JSON_MAP_REFERENCE(UpdateCurrentPedalboardBody, pedalboard) +JSON_MAP_END() + +class SetSnapshotsBody +{ +public: + std::vector> snapshots_; + int64_t selectedSnapshot_; + + DECLARE_JSON_MAP(SetSnapshotsBody); +}; + +JSON_MAP_BEGIN(SetSnapshotsBody) +JSON_MAP_REFERENCE(SetSnapshotsBody, snapshots) +JSON_MAP_REFERENCE(SetSnapshotsBody, selectedSnapshot) +JSON_MAP_END() + +class SetSelectedPedalboardPluginBody +{ +public: + uint64_t clientId_; + uint64_t pluginInstanceId_; + + DECLARE_JSON_MAP(SetSelectedPedalboardPluginBody); +}; + +JSON_MAP_BEGIN(SetSelectedPedalboardPluginBody) +JSON_MAP_REFERENCE(SetSelectedPedalboardPluginBody, clientId) +JSON_MAP_REFERENCE(SetSelectedPedalboardPluginBody, pluginInstanceId) +JSON_MAP_END() + +class SnapshotModifiedBody +{ +public: + int64_t snapshotIndex_; + bool modified_; + + DECLARE_JSON_MAP(SnapshotModifiedBody); +}; + +JSON_MAP_BEGIN(SnapshotModifiedBody) +JSON_MAP_REFERENCE(SnapshotModifiedBody, snapshotIndex) +JSON_MAP_REFERENCE(SnapshotModifiedBody, modified) +JSON_MAP_END() + +class ChannelRouterSettingsChangedBody +{ +public: + ChannelRouterSettingsChangedBody( + int64_t clientId, + const ChannelRouterSettings &channelRouterSettings) : clientId_(clientId), channelRouterSettings_(channelRouterSettings) + { + } + int64_t clientId_ = -1; + ChannelRouterSettings channelRouterSettings_; + + DECLARE_JSON_MAP(ChannelRouterSettingsChangedBody); +}; +JSON_MAP_BEGIN(ChannelRouterSettingsChangedBody) +JSON_MAP_REFERENCE(ChannelRouterSettingsChangedBody, clientId) +JSON_MAP_REFERENCE(ChannelRouterSettingsChangedBody, channelRouterSettings) +JSON_MAP_END() + +class PresetsChangedBody +{ +public: + int64_t clientId_ = -1; + PresetIndex *presets_ = nullptr; + + DECLARE_JSON_MAP(PresetsChangedBody); +}; +JSON_MAP_BEGIN(PresetsChangedBody) +JSON_MAP_REFERENCE(PresetsChangedBody, clientId) +JSON_MAP_REFERENCE(PresetsChangedBody, presets) +JSON_MAP_END() + +class ControlChangedBody +{ +public: + int64_t clientId_; + int64_t instanceId_; + std::string symbol_; + float value_; + + DECLARE_JSON_MAP(ControlChangedBody); +}; + +JSON_MAP_BEGIN(ControlChangedBody) +JSON_MAP_REFERENCE(ControlChangedBody, clientId) +JSON_MAP_REFERENCE(ControlChangedBody, instanceId) +JSON_MAP_REFERENCE(ControlChangedBody, symbol) +JSON_MAP_REFERENCE(ControlChangedBody, value) +JSON_MAP_END() + +class PatchPropertyChangedBody +{ +public: + int64_t clientId_; + int64_t instanceId_; + std::string propertyUri_; + json_variant value_; + + DECLARE_JSON_MAP(PatchPropertyChangedBody); +}; + +JSON_MAP_BEGIN(PatchPropertyChangedBody) +JSON_MAP_REFERENCE(PatchPropertyChangedBody, clientId) +JSON_MAP_REFERENCE(PatchPropertyChangedBody, instanceId) +JSON_MAP_REFERENCE(PatchPropertyChangedBody, propertyUri) +JSON_MAP_REFERENCE(PatchPropertyChangedBody, value) +JSON_MAP_END() + +class Vst3ControlChangedBody +{ +public: + int64_t clientId_; + int64_t instanceId_; + std::string symbol_; + float value_; + std::string state_; + + DECLARE_JSON_MAP(Vst3ControlChangedBody); +}; +JSON_MAP_BEGIN(Vst3ControlChangedBody) +JSON_MAP_REFERENCE(Vst3ControlChangedBody, clientId) +JSON_MAP_REFERENCE(Vst3ControlChangedBody, instanceId) +JSON_MAP_REFERENCE(Vst3ControlChangedBody, symbol) +JSON_MAP_REFERENCE(Vst3ControlChangedBody, value) +JSON_MAP_REFERENCE(Vst3ControlChangedBody, state) +JSON_MAP_END() + +class PiPedalSocketHandler; +namespace +{ + using PfnMessageHandler = void (PiPedalSocketHandler::*)(int replyTo, json_reader *pReader); + + inline static unordered_map socket_messageHandlers; + +} + +class PiPedalSocketHandler : public SocketHandler, public IPiPedalModelSubscriber, public std::enable_shared_from_this +{ +private: + AdminClient &GetAdminClient() const + { + return model.GetAdminClient(); + } + + std::recursive_mutex writeMutex; + PiPedalModel &model; + MixerApi mixerApi; + static std::atomic nextClientId; + std::string imageList; + + uint64_t clientId; + // pedalboard is mutable and not thread-safe. + std::recursive_mutex subscriptionMutex; + struct VuSubscription + { + int64_t subscriptionHandle; + int64_t instanceId; + }; + + bool PingTone3000Server(); + std::vector activeVuSubscriptions; + + struct PortMonitorSubscription + { + PortMonitorSubscription( + int64_t subscriptionHandle, + int64_t instanceId, + const std::string &key) + : subscriptionHandle(subscriptionHandle), + instanceId(instanceId), + key(key) + { + } + ~PortMonitorSubscription() + { + Close(); + } + PortMonitorSubscription() {} + PortMonitorSubscription(const PortMonitorSubscription &) = delete; + PortMonitorSubscription &operator=(const PortMonitorSubscription &) = delete; + void Close() + { + std::lock_guard lock{pmMutex}; + closed = true; + } + + std::mutex pmMutex; + + bool closed = false; + int64_t subscriptionHandle = -1; + int64_t instanceId = -1; + std::string key; + + static constexpr float INVALID_VALUE = std::numeric_limits::max(); + float lastValue = INVALID_VALUE; + float currentValue = INVALID_VALUE; + + bool pendingValue = false; + bool waitingForAck = false; + }; + + std::mutex activePortMonitorsMutex; + std::vector> activePortMonitors; + std::atomic closed = false; + +public: + virtual int64_t GetClientId() { return clientId; } + + virtual ~PiPedalSocketHandler() + { + if (!closed) + { + FinalCleanup(); + } + } + + bool finalCleanup = false; + void FinalCleanup() + { + if (finalCleanup) + return; + finalCleanup = true; + // avoid use after free. + for (int i = 0; i < this->activePortMonitors.size(); ++i) + { + model.UnmonitorPort(activePortMonitors[i]->subscriptionHandle); + } + activePortMonitors.resize(0); + for (int i = 0; i < this->activeVuSubscriptions.size(); ++i) + { + model.RemoveVuSubscription(activeVuSubscriptions[i].subscriptionHandle); + } + activeVuSubscriptions.resize(0); + + model.RemoveNotificationSubsription(shared_from_this()); + // Warning: potentially deleted after return. + } + + virtual void Close() + { + if (closed) + return; + { + auto selfHolder = shared_from_this(); // keep ourselves alive until we return. + closed = true; + + FinalCleanup(); // do it while we can. &model will no longer be valid after this. ( :-( ) + SocketHandler::Close(); + } + } + + PiPedalSocketHandler(PiPedalModel &model) + : model(model), clientId(++nextClientId) + { + // Wire MixerEngine to MixerApi if available + auto engine = model.GetMixerEngine(); + if (engine) { + this->mixerApi.setMixerEngine(engine.get()); + } + + std::stringstream imageList; + const std::filesystem::path &webRoot = model.GetWebRoot() / "img"; + bool firstTime = true; + try + { + for (const auto &entry : std::filesystem::directory_iterator(webRoot)) + { + if (!firstTime) + { + imageList << ";"; + } + firstTime = false; + imageList << entry.path().filename().string(); + } + } + catch (const std::exception &) + { + Lv2Log::error("Can't list files in %s. Image files will not be pre-loaded in the client.", webRoot.c_str()); + } + this->imageList = imageList.str(); + } + +private: + void JsonReply(int replyTo, const char *message, const char *json) + { + std::stringstream s(ios_base::out); + + json_writer writer(s, true); + + writer.start_array(); + { + writer.start_object(); + { + if (replyTo != -1) + { + writer.write_member("reply", replyTo); + writer.write_raw(","); + } + writer.write_member("message", message); + } + writer.end_object(); + writer.write_raw(","); + writer.write_raw(json); + } + writer.end_array(); + + { + std::lock_guard guard(this->writeMutex); + this->send(s.str()); + } + } + // void JsonSend(const char *message, const char *json) + // { + // JsonReply(-1, message, json); + // } + template + void Reply(int replyTo, const char *message, const T &value) + { + std::stringstream s(ios_base::out); + + json_writer writer(s, true); + writer.start_array(); + { + writer.start_object(); + { + if (replyTo != -1) + { + writer.write_member("reply", replyTo); + writer.write_raw(","); + } + writer.write_member("message", message); + } + writer.end_object(); + writer.write_raw(","); + writer.write(value); + } + writer.end_array(); + { + std::lock_guard guard(this->writeMutex); + this->send(s.str()); + } + } + void Reply(int replyTo, const char *message) + { + if (replyTo == -1) + return; + std::stringstream s(ios_base::out); + + json_writer writer(s, true); + writer.start_array(); + { + writer.start_object(); + { + if (replyTo != -1) + { + writer.write_member("reply", replyTo); + writer.write_raw(","); + } + writer.write_member("message", message); + } + writer.end_object(); + } + writer.end_array(); + + { + std::lock_guard guard(this->writeMutex); + this->send(s.str()); + } + } + +private: + class IRequestReservation + { + int reservationId; + + public: + IRequestReservation(int reservationId) + : reservationId(reservationId) + { + } + int GetReservationid() const { return reservationId; } + virtual ~IRequestReservation() {} + virtual void onResult(json_reader *pReader) = 0; + virtual void onError(const std::exception &e) = 0; + }; + + class RequestReservationBase : public IRequestReservation + { + protected: + virtual void onResult(json_reader &reader) = 0; + }; + template + class RequestReservation : public IRequestReservation + { + public: + using ResponseFn = std::function; + using ErrorFn = std::function; + + private: + ResponseFn responseFn; + bool hasErrorFn; + ErrorFn errorFn; + + virtual void onError(const std::exception &e) + { + if (hasErrorFn) + { + errorFn(e); + } + else + { + throw e; + } + }; + + virtual void onResult(json_reader *pReader) + { + REPLY value; + try + { + if (pReader == nullptr) + { + throw PiPedalException("No value provide for reply."); + } + pReader->read(&value); + responseFn(value); + } + catch (const std::exception &e) + { + onError(e); + } + }; + + public: + RequestReservation(int reservationId, ResponseFn onResponse, ErrorFn errorFn) + : IRequestReservation(reservationId), responseFn(onResponse), errorFn(errorFn), hasErrorFn(true) + { + } + RequestReservation(int reservationId, ResponseFn onResponse) + : IRequestReservation(reservationId), responseFn(onResponse), hasErrorFn(false) + { + } + }; + std::recursive_mutex requestMutex; + std::vector requestReservations; + std::atomic nextRequestId{1}; + +public: + template + void Request(const char *message, const T &body, + std::function onSuccess, + std::function onError) + { + try + { + RequestReservation *reservation = new RequestReservation( + (int)++nextRequestId, + onSuccess, + onError); + { + std::lock_guard lock(requestMutex); + requestReservations.push_back(reservation); + } + std::stringstream s(ios_base::out); + + json_writer writer(s, true); + writer.start_array(); + { + writer.start_object(); + { + writer.write_member("replyTo", reservation->GetReservationid()); + writer.write_raw(","); + writer.write_member("message", message); + } + writer.end_object(); + writer.write_raw(","); + writer.write(body); + } + writer.end_array(); + { + std::lock_guard guard(this->writeMutex); + this->send(s.str()); + } + } + catch (const std::exception &e) + { + onError(PiPedalException(e.what())); + } + } + + template + void Send(const char *message, const T &body) + { + Reply(-1, message, body); + } + void Send(const char *message) + { + Reply(-1, message); + } + + void SendError(int replyTo, std::exception &e) + { + Reply(replyTo, "error", e.what()); + } + void SendError(int replyTo, const std::string &what) + { + Reply(replyTo, "error", what); + } + + std::shared_ptr getPortMonitorSubscription(uint64_t subscriptionId) + { + std::shared_ptr result; + { + std::lock_guard lock(activePortMonitorsMutex); + + for (int i = 0; i < this->activePortMonitors.size(); ++i) + { + if (activePortMonitors[i]->subscriptionHandle == subscriptionId) + { + result = activePortMonitors[i]; + break; + } + } + } + return result; + } + + void SendMonitorPortMessage(std::shared_ptr &subscription, float value) + { + // running on RT_output thread, or on Socket thread. + { + std::lock_guard lock{subscription->pmMutex}; + if (subscription->closed) + return; + if (value == subscription->currentValue) + return; + if (subscription->waitingForAck) + { + subscription->pendingValue = true; + subscription->currentValue = value; + return; + } + subscription->currentValue = value; + subscription->lastValue = value; + subscription->waitingForAck = true; + } + SendMonitorPortMessage_Inner(subscription, value); + } + + // post-sync send. + void SendMonitorPortMessage_Inner(std::shared_ptr &subscription, float value) + { + auto subscriptionHandle_ = subscription->subscriptionHandle; + MonitorResultBody body; + body.subscriptionHandle_ = subscriptionHandle_; + body.value_ = value; + this->Request( // send the message and wait for a response. + "onMonitorPortOutput", + body, + [this, subscriptionHandle_](const bool &result) + { + // running on PiPedalSocket thread. + std::shared_ptr subscription = getPortMonitorSubscription(subscriptionHandle_); + if (!subscription) + return; + + float value; + { + std::unique_lock lock{subscription->pmMutex}; + if (subscription->closed) + return; + if (subscription->pendingValue) + { + value = subscription->currentValue; + subscription->lastValue = value; + subscription->pendingValue = false; + subscription->waitingForAck = true; + + lock.unlock(); + + SendMonitorPortMessage_Inner(subscription, value); + return; + } + else + { + subscription->waitingForAck = false; + } + } + }, + [](const std::exception &e) + { + Lv2Log::debug("Failed to monitor port output. (%s)", e.what()); + }); + } + void MonitorPort(int replyTo, MonitorPortBody &body) + { + std::lock_guard guard(subscriptionMutex); + int64_t subscriptionHandle = model.MonitorPort( + body.instanceId_, + body.key_, + body.updateRate_, + [this](int64_t subscriptionHandle_, float value) + { + std::shared_ptr subscription = getPortMonitorSubscription(subscriptionHandle_); + + if (subscription) + { + SendMonitorPortMessage(subscription, value); + } + } + + ); + { + std::lock_guard lock(activePortMonitorsMutex); + activePortMonitors.push_back(std::make_shared(subscriptionHandle, body.instanceId_, body.key_)); + } + + this->Reply(replyTo, "monitorPort", subscriptionHandle); + } + + /***********************/ + + class MessageRegistration + { + public: + MessageRegistration(const std::string &messageName, PfnMessageHandler pfnMessageHandler) + { + socket_messageHandlers[messageName] = pfnMessageHandler; + } + }; + +#define REGISTER_MESSAGE_HANDLER(MESSAGE_NAME) \ + static inline MessageRegistration r_##MESSAGE_NAME{#MESSAGE_NAME, &PiPedalSocketHandler::handle_##MESSAGE_NAME}; + + void handle_setControl(int replyTo, json_reader *pReader) + { + ControlChangedBody message; + pReader->read(&message); + this->model.SetControl(message.clientId_, message.instanceId_, message.symbol_, message.value_); + } + REGISTER_MESSAGE_HANDLER(setControl) + + + void handle_makeTone3000Pkce(int replyTo, json_reader *pReader) + { + std::string redirectUrl; + pReader->read(&redirectUrl); + + Tone3000PkceParams result {redirectUrl}; + this->Reply(replyTo, "makeTone3000Pkce", result); + + } + REGISTER_MESSAGE_HANDLER(makeTone3000Pkce); + + + void handle_writeTone3000Readme(int replyTo, json_reader*pReader) + { + WriteTone3000ReadmeBody body; + pReader->read(&body); + model.WriteTone3000Readme(body.filePath_, body.tone_,body.thumbnailUrl_); + } + REGISTER_MESSAGE_HANDLER(writeTone3000Readme) + + /************************************************************************/ + /* Band-in-a-Box Mixer Messages */ + /************************************************************************/ + + void handle_mixerSetChannelVolume(int replyTo, json_reader *pReader) + { + int channelIndex; + double volume; + pReader->read(&channelIndex); + pReader->read(&volume); + this->mixerApi.setChannelVolume(channelIndex, (float)volume); + } + REGISTER_MESSAGE_HANDLER(mixerSetChannelVolume) + + void handle_mixerSetChannelPan(int replyTo, json_reader *pReader) + { + int channelIndex; + double pan; + pReader->read(&channelIndex); + pReader->read(&pan); + this->mixerApi.setChannelPan(channelIndex, (float)pan); + } + REGISTER_MESSAGE_HANDLER(mixerSetChannelPan) + + void handle_mixerSetChannelMute(int replyTo, json_reader *pReader) + { + int channelIndex; + bool mute; + pReader->read(&channelIndex); + pReader->read(&mute); + this->mixerApi.setChannelMute(channelIndex, mute); + } + REGISTER_MESSAGE_HANDLER(mixerSetChannelMute) + + void handle_mixerSetChannelSolo(int replyTo, json_reader *pReader) + { + int channelIndex; + bool solo; + pReader->read(&channelIndex); + pReader->read(&solo); + this->mixerApi.setChannelSolo(channelIndex, solo); + } + REGISTER_MESSAGE_HANDLER(mixerSetChannelSolo) + + void handle_mixerSetChannelLabel(int replyTo, json_reader *pReader) + { + int channelIndex; + std::string label; + pReader->read(&channelIndex); + pReader->read(&label); + this->mixerApi.setChannelLabel(channelIndex, label); + } + REGISTER_MESSAGE_HANDLER(mixerSetChannelLabel) + + void handle_mixerSetChannelHpf(int replyTo, json_reader *pReader) + { + int channelIndex; + bool enabled; + double frequency; + pReader->read(&channelIndex); + pReader->read(&enabled); + pReader->read(&frequency); + this->mixerApi.setChannelHpf(channelIndex, enabled, (float)frequency); + } + REGISTER_MESSAGE_HANDLER(mixerSetChannelHpf) + + void handle_mixerGetState(int replyTo, json_reader *pReader) + { + std::string stateJson = this->mixerApi.getStateJson(); + this->JsonReply(replyTo, "mixerState", stateJson.c_str()); + } + REGISTER_MESSAGE_HANDLER(mixerGetState) + + void handle_mixerAddChannel(int replyTo, json_reader *pReader) + { + int physicalInputIndex; + pReader->read(&physicalInputIndex); + int channelIndex = this->mixerApi.addChannel(physicalInputIndex); + this->Reply(replyTo, "mixerAddChannel", (int64_t)channelIndex); + } + REGISTER_MESSAGE_HANDLER(mixerAddChannel) + + void handle_mixerRemoveChannel(int replyTo, json_reader *pReader) + { + int channelIndex; + pReader->read(&channelIndex); + this->mixerApi.removeChannel(channelIndex); + } + REGISTER_MESSAGE_HANDLER(mixerRemoveChannel) + + void handle_mixerSetBusVolume(int replyTo, json_reader *pReader) + { + int64_t busId; + double volume; + pReader->read(&busId); + pReader->read(&volume); + this->mixerApi.setBusVolume(busId, (float)volume); + } + REGISTER_MESSAGE_HANDLER(mixerSetBusVolume) + + void handle_mixerSetBusMute(int replyTo, json_reader *pReader) + { + int64_t busId; + bool mute; + pReader->read(&busId); + pReader->read(&mute); + this->mixerApi.setBusMute(busId, mute); + } + REGISTER_MESSAGE_HANDLER(mixerSetBusMute) + + void handle_mixerRouteChannelToBus(int replyTo, json_reader *pReader) + { + int channelIndex; + int64_t busId; + double level; + pReader->read(&channelIndex); + pReader->read(&busId); + pReader->read(&level); + this->mixerApi.routeChannelToBus(channelIndex, busId, (float)level); + } + REGISTER_MESSAGE_HANDLER(mixerRouteChannelToBus) + + void handle_mixerAddBus(int replyTo, json_reader *pReader) + { + std::string type; + std::string name; + int channels; + pReader->read(&type); + pReader->read(&name); + pReader->read(&channels); + int64_t busId = this->mixerApi.addBus(type, name, channels); + this->Reply(replyTo, "mixerAddBus", busId); + } + REGISTER_MESSAGE_HANDLER(mixerAddBus) + + void handle_mixerRemoveBus(int replyTo, json_reader *pReader) + { + int64_t busId; + pReader->read(&busId); + this->mixerApi.removeBus(busId); + } + REGISTER_MESSAGE_HANDLER(mixerRemoveBus) + + void handle_mixerSaveScene(int replyTo, json_reader *pReader) + { + int64_t sceneId; + std::string name; + pReader->read(&sceneId); + pReader->read(&name); + std::string result = this->mixerApi.saveScene(name); + this->JsonReply(replyTo, "mixerSaveScene", result.c_str()); + } + REGISTER_MESSAGE_HANDLER(mixerSaveScene) + + void handle_mixerLoadScene(int replyTo, json_reader *pReader) + { + int64_t sceneId; + pReader->read(&sceneId); + bool ok = this->mixerApi.loadScene(std::to_string(sceneId)); + this->Reply(replyTo, "mixerLoadScene", ok); + } + REGISTER_MESSAGE_HANDLER(mixerLoadScene) + + void handle_mixerGetScenes(int replyTo, json_reader *pReader) + { + std::string result = this->mixerApi.listScenes(); + this->JsonReply(replyTo, "mixerGetScenes", result.c_str()); + } + REGISTER_MESSAGE_HANDLER(mixerGetScenes) + + /************************************************************************/ + /* Mixer Output Routing Messages */ + /************************************************************************/ + + void handle_mixerGetOutputRoutes(int replyTo, json_reader *pReader) + { + std::string json = this->mixerApi.getOutputRoutesJson(); + this->JsonReply(replyTo, "mixerOutputRoutes", json.c_str()); + } + REGISTER_MESSAGE_HANDLER(mixerGetOutputRoutes) + + void handle_mixerSetOutputRoutes(int replyTo, json_reader *pReader) + { + std::string json; + pReader->read(&json); + this->mixerApi.setOutputRoutesFromJson(json); + } + REGISTER_MESSAGE_HANDLER(mixerSetOutputRoutes) + + /************************************************************************/ + /* MIDI Control Surface Mapping Messages */ + /************************************************************************/ + + void handle_mixerGetMidiMappings(int replyTo, json_reader *pReader) + { + std::string json = this->mixerApi.getMidiMappingsJson(); + this->JsonReply(replyTo, "mixerMidiMappings", json.c_str()); + } + REGISTER_MESSAGE_HANDLER(mixerGetMidiMappings) + + void handle_mixerSetMidiMappings(int replyTo, json_reader *pReader) + { + std::string json; + pReader->read(&json); + this->mixerApi.setMidiMappingsFromJson(json); + } + REGISTER_MESSAGE_HANDLER(mixerSetMidiMappings) + + void handle_mixerAddMidiMapping(int replyTo, json_reader *pReader) + { + int midiChannel; + int ccNumber; + std::string targetType; + int64_t targetId; + double minValue = 0.0; + double maxValue = 1.0; + pReader->read(&midiChannel); + pReader->read(&ccNumber); + pReader->read(&targetType); + pReader->read(&targetId); + pReader->read(&minValue); + pReader->read(&maxValue); + this->mixerApi.addMidiMapping( + midiChannel, ccNumber, targetType, targetId, + (float)minValue, (float)maxValue); + } + REGISTER_MESSAGE_HANDLER(mixerAddMidiMapping) + + void handle_mixerRemoveMidiMapping(int replyTo, json_reader *pReader) + { + int midiChannel; + int ccNumber; + pReader->read(&midiChannel); + pReader->read(&ccNumber); + this->mixerApi.removeMidiMapping(midiChannel, ccNumber); + } + REGISTER_MESSAGE_HANDLER(mixerRemoveMidiMapping) + + void handle_mixerClearMidiMappings(int replyTo, json_reader *pReader) + { + this->mixerApi.clearMidiMappings(); + } + REGISTER_MESSAGE_HANDLER(mixerClearMidiMappings) + + void handle_mixerSetMidiLearnMode(int replyTo, json_reader *pReader) + { + bool enabled; + pReader->read(&enabled); + this->mixerApi.setMidiLearnMode(enabled); + } + REGISTER_MESSAGE_HANDLER(mixerSetMidiLearnMode) + + void handle_mixerSetMidiLearnTarget(int replyTo, json_reader *pReader) + { + std::string targetType; + int64_t targetId; + pReader->read(&targetType); + pReader->read(&targetId); + this->mixerApi.setMidiLearnTarget(targetType, targetId); + } + REGISTER_MESSAGE_HANDLER(mixerSetMidiLearnTarget) + + void handle_mixerCommitMidiLearn(int replyTo, json_reader *pReader) + { + bool success = this->mixerApi.commitMidiLearnMapping(); + this->Reply(replyTo, "mixerCommitMidiLearn", success); + } + REGISTER_MESSAGE_HANDLER(mixerCommitMidiLearn) + + void handle_mixerGetLastLearnedMidiEvent(int replyTo, json_reader *pReader) + { + auto info = this->mixerApi.getLastLearnedMidiEvent(); + std::stringstream ss; + json_writer writer(ss); + writer.start_object(); + writer.write_member("hasEvent", info.hasEvent); + writer.write_member("midiChannel", (int64_t)info.midiChannel); + writer.write_member("ccNumber", (int64_t)info.ccNumber); + writer.end_object(); + this->JsonReply(replyTo, "mixerLastLearnedMidiEvent", ss.str().c_str()); + } + REGISTER_MESSAGE_HANDLER(mixerGetLastLearnedMidiEvent) + + void handle_sha256Base64url(int replyTo, json_reader *pReader) + { + std::string input; + pReader->read(&input); + + std::string result = Sha256Base64Url(input); + this->Reply(replyTo, "sha256Base64url", result); + + } + REGISTER_MESSAGE_HANDLER(sha256Base64url); + + void handle_previewControl(int replyTo, json_reader *pReader) + { + ControlChangedBody message; + pReader->read(&message); + this->model.PreviewControl(message.clientId_, message.instanceId_, message.symbol_, message.value_); + } + REGISTER_MESSAGE_HANDLER(previewControl) + + void handle_setInputVolume(int replyTo, json_reader *pReader) + { + float value; + pReader->read(&value); + this->model.SetInputVolume(value); + } + REGISTER_MESSAGE_HANDLER(setInputVolume) + + void handle_setOutputVolume(int replyTo, json_reader *pReader) + { + float value; + pReader->read(&value); + this->model.SetOutputVolume(value); + } + REGISTER_MESSAGE_HANDLER(setOutputVolume) + + void handle_previewInputVolume(int replyTo, json_reader *pReader) + { + float value; + pReader->read(&value); + this->model.PreviewInputVolume(value); + } + REGISTER_MESSAGE_HANDLER(previewInputVolume) + + void handle_previewOutputVolume(int replyTo, json_reader *pReader) + { + float value; + pReader->read(&value); + this->model.PreviewOutputVolume(value); + } + REGISTER_MESSAGE_HANDLER(previewOutputVolume) + + void handle_listenForMidiEvent(int replyTo, json_reader *pReader) + { + ListenForMidiEventBody body; + pReader->read(&body); + this->model.ListenForMidiEvent(this->clientId, body.handle_); + } + REGISTER_MESSAGE_HANDLER(listenForMidiEvent) + + void handle_cancelListenForMidiEvent(int replyTo, json_reader *pReader) + { + uint64_t handle; + pReader->read(&handle); + this->model.CancelListenForMidiEvent(this->clientId, handle); + } + REGISTER_MESSAGE_HANDLER(cancelListenForMidiEvent) + + void handle_monitorPatchProperty(int replyTo, json_reader *pReader) + { + MonitorPatchPropertyBody body; + pReader->read(&body); + this->model.MonitorPatchProperty(this->clientId, body.clientHandle_, body.instanceId_, body.propertyUri_); + } + REGISTER_MESSAGE_HANDLER(monitorPatchProperty) + + void handle_cancelMonitorPatchProperty(int replyTo, json_reader *pReader) + { + int64_t handle; + pReader->read(&handle); + this->model.CancelMonitorPatchProperty(this->clientId, handle); + } + REGISTER_MESSAGE_HANDLER(cancelMonitorPatchProperty) + + void handle_getUpdateStatus(int replyTo, json_reader *pReader) + { + UpdateStatus updateStatus = model.GetUpdateStatus(); + this->Reply(replyTo, "getUpdateStatus", updateStatus); + } + REGISTER_MESSAGE_HANDLER(getUpdateStatus) + + void handle_getHasWifi(int replyTo, json_reader *pReader) + { + bool result = model.GetHasWifi(); + this->Reply(replyTo, "getHasWifi", result); + } + REGISTER_MESSAGE_HANDLER(getHasWifi) + + void handle_updateNow(int replyTo, json_reader *pReader) + { + std::string updateUrl; + pReader->read(&updateUrl); + model.UpdateNow(updateUrl); + bool result = true; + this->Reply(replyTo, "updateNow", result); + } + REGISTER_MESSAGE_HANDLER(updateNow) + + void handle_getJackStatus(int replyTo, json_reader *pReader) + { + JackHostStatus status = model.GetJackStatus(); + this->Reply(replyTo, "getJackStatus", status); + } + REGISTER_MESSAGE_HANDLER(getJackStatus) + + void handle_getAlsaDevices(int replyTo, json_reader *pReader) + { + std::vector devices = model.GetAlsaDevices(); + this->Reply(replyTo, "getAlsaDevices", devices); + } + REGISTER_MESSAGE_HANDLER(getAlsaDevices) + + void handle_getKnownWifiNetworks(int replyTo, json_reader *pReader) + { + std::vector channels = this->model.GetKnownWifiNetworks(); + this->Reply(replyTo, "getWifiChannels", channels); + } + REGISTER_MESSAGE_HANDLER(getKnownWifiNetworks) + + void handle_getWifiChannels(int replyTo, json_reader *pReader) + { + std::string country; + pReader->read(&country); + std::vector channels = pipedal::getWifiChannelSelectors(country.c_str()); + this->Reply(replyTo, "getWifiChannels", channels); + } + REGISTER_MESSAGE_HANDLER(getWifiChannels) + + void handle_getPluginPresets(int replyTo, json_reader *pReader) + { + std::string uri; + pReader->read(&uri); + this->Reply(replyTo, "getPluginPresets", this->model.GetPluginUiPresets(uri)); + } + REGISTER_MESSAGE_HANDLER(getPluginPresets) + + void handle_loadPluginPreset(int replyTo, json_reader *pReader) + { + LoadPluginPresetBody body; + pReader->read(&body); + this->model.LoadPluginPreset(body.pluginInstanceId_, body.presetInstanceId_); + } + REGISTER_MESSAGE_HANDLER(loadPluginPreset) + + void handle_setJackServerSettings(int replyTo, json_reader *pReader) + { + JackServerSettings jackServerSettings; + pReader->read(&jackServerSettings); + this->model.SetJackServerSettings(jackServerSettings); + this->Reply(replyTo, "setJackserverSettings"); + } + REGISTER_MESSAGE_HANDLER(setJackServerSettings) + + void handle_setGovernorSettings(int replyTo, json_reader *pReader) + { + std::string governor; + pReader->read(&governor); + std::string fromAddress = this->getFromAddress(); + // if (!IsOnLocalSubnet(fromAddress)) + // { + // throw PiPedalException("Permission denied. Not on local subnet."); + // } + this->model.SetGovernorSettings(governor); + this->Reply(replyTo, "setGovernorSettings"); + } + REGISTER_MESSAGE_HANDLER(setGovernorSettings) + + void handle_setWifiConfigSettings(int replyTo, json_reader *pReader) + { + WifiConfigSettings wifiConfigSettings; + pReader->read(&wifiConfigSettings); + if (!GetAdminClient().CanUseAdminClient()) + { + throw PiPedalException("Can't change server settings when running interactively."); + } + std::string fromAddress = this->getFromAddress(); + // if (!IsOnLocalSubnet(fromAddress)) + // { + // throw PiPedalException("Permission denied. Not on local subnet."); + // } + + this->model.SetWifiConfigSettings(wifiConfigSettings); + this->Reply(replyTo, "setWifiConfigSettings"); + } + REGISTER_MESSAGE_HANDLER(setWifiConfigSettings) + + void handle_getWifiConfigSettings(int replyTo, json_reader *pReader) + { + this->Reply(replyTo, "getWifiConfigSettings", model.GetWifiConfigSettings()); + } + REGISTER_MESSAGE_HANDLER(getWifiConfigSettings) + + void handle_setWifiDirectConfigSettings(int replyTo, json_reader *pReader) + { + WifiDirectConfigSettings wifiDirectConfigSettings; + pReader->read(&wifiDirectConfigSettings); + if (!GetAdminClient().CanUseAdminClient()) + { + throw PiPedalException("Can't change server settings when running interactively."); + } + std::string fromAddress = this->getFromAddress(); + // if (!IsOnLocalSubnet(fromAddress)) + // { + // throw PiPedalException("Permission denied. Not on local subnet."); + // } + + this->model.SetWifiDirectConfigSettings(wifiDirectConfigSettings); + this->Reply(replyTo, "setWifiDirectConfigSettings"); + } + REGISTER_MESSAGE_HANDLER(setWifiDirectConfigSettings) + + void handle_getWifiDirectConfigSettings(int replyTo, json_reader *pReader) + { + this->Reply(replyTo, "getWifiDirectConfigSettings", model.GetWifiDirectConfigSettings()); + } + REGISTER_MESSAGE_HANDLER(getWifiDirectConfigSettings) + + void handle_getGovernorSettings(int replyTo, json_reader *pReader) + { + this->Reply(replyTo, "getGovernorSettings", model.GetGovernorSettings()); + } + REGISTER_MESSAGE_HANDLER(getGovernorSettings) + + void handle_getJackServerSettings(int replyTo, json_reader *pReader) + { + this->Reply(replyTo, "getJackServerSettings", model.GetJackServerSettings()); + } + REGISTER_MESSAGE_HANDLER(getJackServerSettings) + + void handle_getBankIndex(int replyTo, json_reader *pReader) + { + BankIndex bankIndex = model.GetBankIndex(); + this->Reply(replyTo, "getBankIndex", bankIndex); + } + REGISTER_MESSAGE_HANDLER(getBankIndex) + + void handle_getJackConfiguration(int replyTo, json_reader *pReader) + { + JackConfiguration configuration = this->model.GetJackConfiguration(); + this->Reply(replyTo, "getJackConfiguration", configuration); + } + REGISTER_MESSAGE_HANDLER(getJackConfiguration) + + void handle_getJackSettings(int replyTo, json_reader *pReader) + { + JackChannelSelection selection = this->model.GetJackChannelSelection(); + this->Reply(replyTo, "getJackSettings", selection); + } + REGISTER_MESSAGE_HANDLER(getJackSettings) + + void handle_saveCurrentPreset(int replyTo, json_reader *pReader) + { + this->model.SaveCurrentPreset(this->clientId); + } + REGISTER_MESSAGE_HANDLER(saveCurrentPreset) + + void handle_saveCurrentPresetAs(int replyTo, json_reader *pReader) + { + SaveCurrentPresetAsBody body; + pReader->read(&body); + int64_t result = this->model.SaveCurrentPresetAs(this->clientId, body.bankInstanceId_, body.name_, body.saveAfterInstanceId_); + Reply(replyTo, "saveCurrentPresetsAs", result); + } + REGISTER_MESSAGE_HANDLER(saveCurrentPresetAs) + + void handle_setSelectedPedalboardPlugin(int replyTo, json_reader *pReader) + { + SetSelectedPedalboardPluginBody body; + pReader->read(&body); + this->model.SetSelectedPedalboardPlugin(body.clientId_, body.pluginInstanceId_); + } + REGISTER_MESSAGE_HANDLER(setSelectedPedalboardPlugin) + + void handle_savePluginPresetAs(int replyTo, json_reader *pReader) + { + SavePluginPresetAsBody body; + pReader->read(&body); + int64_t result = this->model.SavePluginPresetAs(body.instanceId_, body.name_); + Reply(replyTo, "saveCurrentPresetsAs", result); + } + REGISTER_MESSAGE_HANDLER(savePluginPresetAs) + + void handle_getPresets(int replyTo, json_reader *pReader) + { + PresetIndex presets; + this->model.GetPresets(&presets); + Reply(replyTo, "getPresets", presets); + } + REGISTER_MESSAGE_HANDLER(getPresets) + + void handle_setPedalboardItemEnable(int replyTo, json_reader *pReader) + { + PedalboardItemEnabledBody body; + pReader->read(&body); + model.SetPedalboardItemEnable(body.clientId_, body.instanceId_, body.enabled_); + } + REGISTER_MESSAGE_HANDLER(setPedalboardItemEnable) + + void handle_setPedalboardItemUseModUi(int replyTo, json_reader *pReader) + { + PedalboardItemUseModGuiBody body; + pReader->read(&body); + model.SetPedalboardItemUseModUi(body.clientId_, body.instanceId_, body.useModUi_); + } + REGISTER_MESSAGE_HANDLER(setPedalboardItemUseModUi) + + void handle_updateCurrentPedalboard(int replyTo, json_reader *pReader) + { + UpdateCurrentPedalboardBody body; + + pReader->read(&body); + this->model.UpdateCurrentPedalboard(body.clientId_, body.pedalboard_); + } + REGISTER_MESSAGE_HANDLER(updateCurrentPedalboard) + + void handle_setSnapshot(int replyTo, json_reader *pReader) + { + int64_t snapshotIndex = -1; + pReader->read(&snapshotIndex); + this->model.SetSnapshot(snapshotIndex); + } + REGISTER_MESSAGE_HANDLER(setSnapshot) + + void handle_setSnapshots(int replyTo, json_reader *pReader) + { + SetSnapshotsBody body; + pReader->read(&body); + this->model.SetSnapshots(body.snapshots_, body.selectedSnapshot_); + } + REGISTER_MESSAGE_HANDLER(setSnapshots) + + void handle_currentPedalboard(int replyTo, json_reader *pReader) + { + auto pedalboard = model.GetCurrentPedalboardCopy(); + Reply(replyTo, "currentPedalboard", pedalboard); + } + REGISTER_MESSAGE_HANDLER(currentPedalboard) + + void handle_plugins(int replyTo, json_reader *pReader) + { + auto ui_plugins = model.GetPluginHost().GetUiPlugins(); + Reply(replyTo, "plugins", ui_plugins); + } + REGISTER_MESSAGE_HANDLER(plugins) + + void handle_pluginClasses(int replyTo, json_reader *pReader) + { + auto classes = model.GetPluginHost().GetLv2PluginClass(); + Reply(replyTo, "pluginClasses", classes); + } + REGISTER_MESSAGE_HANDLER(pluginClasses) + + void handle_hello(int replyTo, json_reader *pReader) + { + this->model.AddNotificationSubscription(shared_from_this()); + Reply(replyTo, "ehlo", clientId); + } + REGISTER_MESSAGE_HANDLER(hello) + + void handle_setShowStatusMonitor(int replyTo, json_reader *pReader) + { + bool showStatusMonitor; + pReader->read(&showStatusMonitor); + this->model.SetShowStatusMonitor(showStatusMonitor); + } + REGISTER_MESSAGE_HANDLER(setShowStatusMonitor) + + void handle_getShowStatusMonitor(int replyTo, json_reader *pReader) + { + Reply(replyTo, "getShowStatusMonitor", this->model.GetShowStatusMonitor()); + } + REGISTER_MESSAGE_HANDLER(getShowStatusMonitor) + + void handle_version(int replyTo, json_reader *pReader) + { + PiPedalVersion version(this->model); + + Reply(replyTo, "version", version); + } + REGISTER_MESSAGE_HANDLER(version) + + void handle_loadPreset(int replyTo, json_reader *pReader) + { + int64_t instanceId = 0; + pReader->read(&instanceId); + model.LoadPreset(this->clientId, instanceId); + } + REGISTER_MESSAGE_HANDLER(loadPreset) + + void handle_updatePresets(int replyTo, json_reader *pReader) + { + PresetIndex newIndex; + pReader->read(&newIndex); + bool result = model.UpdatePresets(this->clientId, newIndex); + this->Reply(replyTo, "updatePresets", result); + } + REGISTER_MESSAGE_HANDLER(updatePresets) + + void handle_updatePluginPresets(int replyTo, json_reader *pReader) + { + PluginUiPresets pluginPresets; + pReader->read(&pluginPresets); + model.UpdatePluginPresets(pluginPresets); + this->Reply(replyTo, "updatePluginPresets", true); + } + REGISTER_MESSAGE_HANDLER(updatePluginPresets) + + void handle_moveBank(int replyTo, json_reader *pReader) + { + FromToBody body; + pReader->read(&body); + model.MoveBank(this->clientId, body.from_, body.to_); + this->Reply(replyTo, "moveBank"); + } + REGISTER_MESSAGE_HANDLER(moveBank) + + void handle_shutdown(int replyTo, json_reader *pReader) + { + model.RequestShutdown(false); + this->Reply(replyTo, "shutdown"); + } + REGISTER_MESSAGE_HANDLER(shutdown) + + void handle_restart(int replyTo, json_reader *pReader) + { + model.RequestShutdown(true); + this->Reply(replyTo, "restart"); + } + REGISTER_MESSAGE_HANDLER(restart) + + void handle_deletePresetItems(int replyTo, json_reader *pReader) + { + std::vector items; + pReader->read(&items); + int64_t result = model.DeletePresets(this->clientId, items); + this->Reply(replyTo, "deletePresetItems", result); + } + REGISTER_MESSAGE_HANDLER(deletePresetItems) + + void handle_deleteBankItem(int replyTo, json_reader *pReader) + { + int64_t instanceId = 0; + pReader->read(&instanceId); + uint64_t result = model.DeleteBank(this->clientId, instanceId); + this->Reply(replyTo, "deleteBankItem", result); + } + REGISTER_MESSAGE_HANDLER(deleteBankItem) + + void handle_renameBank(int replyTo, json_reader *pReader) + { + RenameBankBody body; + pReader->read(&body); + + std::stringstream tOut; + json_writer tWriter(tOut); + tWriter.write(body.newName_); + std::string tJson = tOut.str(); + std::stringstream tIn(tJson); + json_reader tReader(tIn); + std::string tResult; + tReader.read(&tResult); + + body.newName_ = tResult; + + try + { + model.RenameBank(this->clientId, body.bankId_, body.newName_); + this->Reply(replyTo, "renameBank"); + } + catch (const std::exception &e) + { + this->SendError(replyTo, std::string(e.what())); + } + } + REGISTER_MESSAGE_HANDLER(renameBank) + + void handle_openBank(int replyTo, json_reader *pReader) + { + int64_t bankId = -1; + pReader->read(&bankId); + try + { + model.OpenBank(this->clientId, bankId); + ; + this->Reply(replyTo, "openBank"); + } + catch (const std::exception &e) + { + this->SendError(replyTo, std::string(e.what())); + } + } + REGISTER_MESSAGE_HANDLER(openBank) + + void handle_saveBankAs(int replyTo, json_reader *pReader) + { + RenameBankBody body; + pReader->read(&body); + try + { + int64_t newId = model.SaveBankAs(this->clientId, body.bankId_, body.newName_); + this->Reply(replyTo, "saveBankAs", newId); + } + catch (const std::exception &e) + { + this->SendError(replyTo, std::string(e.what())); + } + } + REGISTER_MESSAGE_HANDLER(saveBankAs) + + void handle_nextBank(int replyTo, json_reader *pReader) + { + model.NextBank(); + } + REGISTER_MESSAGE_HANDLER(nextBank) + + void handle_previousBank(int replyTo, json_reader *pReader) + { + model.PreviousBank(); + } + REGISTER_MESSAGE_HANDLER(previousBank) + + void handle_nextPreset(int replyTo, json_reader *pReader) + { + model.NextPreset(); + } + REGISTER_MESSAGE_HANDLER(nextPreset) + + void handle_previousPreset(int replyTo, json_reader *pReader) + { + model.PreviousPreset(); + } + REGISTER_MESSAGE_HANDLER(previousPreset) + + void handle_renamePresetItem(int replyTo, json_reader *pReader) + { + RenamePresetBody body; + pReader->read(&body); + + bool result = model.RenamePreset(body.clientId_, body.instanceId_, body.name_); + this->Reply(replyTo, "renamePresetItem", result); + } + REGISTER_MESSAGE_HANDLER(renamePresetItem) + + void handle_copyPreset(int replyTo, json_reader *pReader) + { + CopyPresetBody body; + pReader->read(&body); + int64_t result = model.CopyPreset(body.clientId_, body.fromId_, body.toId_); + this->Reply(replyTo, "copyPreset", result); + } + REGISTER_MESSAGE_HANDLER(copyPreset) + + void handle_copyPluginPreset(int replyTo, json_reader *pReader) + { + CopyPluginPresetBody body; + pReader->read(&body); + uint64_t result = model.CopyPluginPreset(body.pluginUri_, body.instanceId_); + this->Reply(replyTo, "copyPluginPreset", result); + } + REGISTER_MESSAGE_HANDLER(copyPluginPreset) + + void handle_setPatchProperty(int replyTo, json_reader *pReader) + { + SetPatchPropertyBody body; + pReader->read(&body); + model.SendSetPatchProperty(clientId, body.instanceId_, body.propertyUri_, body.value_, [this, replyTo]() + { this->JsonReply(replyTo, "setPatchProperty", "true"); }, [this, replyTo](const std::string &error) + { this->SendError(replyTo, error.c_str()); }); + } + REGISTER_MESSAGE_HANDLER(setPatchProperty) + + void handle_setPedalboardItemTitle(int replyTo, json_reader *pReader) + { + SetPedalboardItemTitleBody body; + pReader->read(&body); + model.SetPedalboardItemTitle(body.instanceId_, body.title_, body.colorKey_); + } + REGISTER_MESSAGE_HANDLER(setPedalboardItemTitle) + + void handle_getPatchProperty(int replyTo, json_reader *pReader) + { + GetPatchPropertyBody body; + pReader->read(&body); + + model.SendGetPatchProperty( + this->clientId, + body.instanceId_, + body.propertyUri_, + [this, replyTo](const std::string &jsonResult) + { + this->JsonReply(replyTo, "getPatchProperty", jsonResult.c_str()); + }, + [this, replyTo](const std::string &error) + { + this->SendError(replyTo, error.c_str()); + }); + } + REGISTER_MESSAGE_HANDLER(getPatchProperty) + + void handle_monitorPort(int replyTo, json_reader *pReader) + { + MonitorPortBody body; + pReader->read(&body); + + MonitorPort(replyTo, body); + } + REGISTER_MESSAGE_HANDLER(monitorPort) + + void handle_unmonitorPort(int replyTo, json_reader *pReader) + { + int64_t subscriptionHandle; + pReader->read(&subscriptionHandle); + { + { + std::lock_guard guard(activePortMonitorsMutex); + for (auto i = this->activePortMonitors.begin(); i != this->activePortMonitors.end(); ++i) + { + if ((*i)->subscriptionHandle == subscriptionHandle) + { + auto subscription = (*i); + subscription->Close(); + + this->activePortMonitors.erase(i); + break; + } + } + } + model.UnmonitorPort(subscriptionHandle); + } + } + REGISTER_MESSAGE_HANDLER(unmonitorPort) + + void handle_addVuSubscription(int replyTo, json_reader *pReader) + { + int64_t instanceId = -1; + + pReader->read(&instanceId); + + int64_t subscriptionHandle = model.AddVuSubscription(instanceId); + + { + std::lock_guard guard(subscriptionMutex); + activeVuSubscriptions.push_back(VuSubscription{subscriptionHandle, instanceId}); + } + this->Reply(replyTo, "addVuSubscription", subscriptionHandle); + } + REGISTER_MESSAGE_HANDLER(addVuSubscription) + + void handle_removeVuSubscription(int replyTo, json_reader *pReader) + { + int64_t subscriptionHandle = -1; + pReader->read(&subscriptionHandle); + { + std::lock_guard guard(subscriptionMutex); + + for (auto i = activeVuSubscriptions.begin(); i != activeVuSubscriptions.end(); ++i) + { + if (i->subscriptionHandle == subscriptionHandle) + { + activeVuSubscriptions.erase(i); + break; + } + } + } + model.RemoveVuSubscription(subscriptionHandle); + } + REGISTER_MESSAGE_HANDLER(removeVuSubscription) + + void handle_imageList(int replyTo, json_reader *pReader) + { + this->Reply(replyTo, "imageList", imageList); + } + REGISTER_MESSAGE_HANDLER(imageList) + + void handle_getFavorites(int replyTo, json_reader *pReader) + { + std::map favorites = this->model.GetFavorites(); + this->Reply(replyTo, "getFavorites", favorites); + } + REGISTER_MESSAGE_HANDLER(getFavorites) + + void handle_setFavorites(int replyTo, json_reader *pReader) + { + std::map favorites; + pReader->read(&favorites); + this->model.SetFavorites(favorites); + } + REGISTER_MESSAGE_HANDLER(setFavorites) + + void handle_setUpdatePolicy(int replyTo, json_reader *pReader) + { + int iPolicy; + pReader->read(&iPolicy); + + this->model.SetUpdatePolicy((UpdatePolicyT)iPolicy); + } + REGISTER_MESSAGE_HANDLER(setUpdatePolicy) + + void handle_forceUpdateCheck(int replyTo, json_reader *pReader) + { + this->model.ForceUpdateCheck(); + } + REGISTER_MESSAGE_HANDLER(forceUpdateCheck) + + void handle_setSystemMidiBindings(int replyTo, json_reader *pReader) + { + std::vector bindings; + pReader->read(&bindings); + this->model.SetSystemMidiBindings(bindings); + } + REGISTER_MESSAGE_HANDLER(setSystemMidiBindings) + + void handle_getSystemMidiBindings(int replyTo, json_reader *pReader) + { + std::vector bindings = this->model.GetSystemMidiBidings(); + this->Reply(replyTo, "getSystemMidiBindings", bindings); + } + REGISTER_MESSAGE_HANDLER(getSystemMidiBindings) + + void handle_requestFileList(int replyTo, json_reader *pReader) + { + throw std::runtime_error("No longer implemented."); + } + REGISTER_MESSAGE_HANDLER(requestFileList) + + void handle_requestFileList2(int replyTo, json_reader *pReader) + { + FileRequestArgs requestArgs; + pReader->read(&requestArgs); + FileRequestResult result = this->model.GetFileList2(requestArgs.relativePath_, requestArgs.fileProperty_); + this->Reply(replyTo, "requestFileList2", result); + } + REGISTER_MESSAGE_HANDLER(requestFileList2) + + void handle_newPreset(int replyTo, json_reader *pReader) + { + int64_t presetId = this->model.CreateNewPreset(); + this->Reply(replyTo, "newPreset", presetId); + } + REGISTER_MESSAGE_HANDLER(newPreset) + + void handle_deleteUserFile(int replyTo, json_reader *pReader) + { + std::string fileName; + pReader->read(&fileName); + + this->model.DeleteSampleFile(fileName); + this->Reply(replyTo, "deleteUserFile", true); + } + REGISTER_MESSAGE_HANDLER(deleteUserFile) + + void handle_createNewSampleDirectory(int replyTo, json_reader *pReader) + { + CreateNewSampleDirectoryArgs args; + pReader->read(&args); + + std::string newFileName = this->model.CreateNewSampleDirectory(args.relativePath_, args.uiFileProperty_); + this->Reply(replyTo, "createNewSampleDirectory", newFileName); + } + REGISTER_MESSAGE_HANDLER(createNewSampleDirectory) + + void handle_renameFilePropertyFile(int replyTo, json_reader *pReader) + { + RenameSampleFileArgs args; + pReader->read(&args); + + std::string newFileName = this->model.RenameFilePropertyFile(args.oldRelativePath_, args.newRelativePath_, args.uiFileProperty_); + this->Reply(replyTo, "renameFilePropertyFile", newFileName); + } + REGISTER_MESSAGE_HANDLER(renameFilePropertyFile) + + void handle_copyFilePropertyFile(int replyTo, json_reader *pReader) + { + CopySampleFileArgs args; + pReader->read(&args); + + std::string newFileName = this->model.CopyFilePropertyFile(args.oldRelativePath_, args.newRelativePath_, args.uiFileProperty_, args.overwrite_); + this->Reply(replyTo, "copyFilePropertyFile", newFileName); + } + REGISTER_MESSAGE_HANDLER(copyFilePropertyFile) + + void handle_getFilePropertyDirectoryTree(int replyTo, json_reader *pReader) + { + GetFilePropertyDirectoryTreeArgs args; + pReader->read(&args); + FilePropertyDirectoryTree::ptr result = + model.GetFilePropertydirectoryTree( + args.fileProperty_, + args.selectedPath_); + this->Reply(replyTo, "GetFilePropertydirectoryTree", result); + } + REGISTER_MESSAGE_HANDLER(getFilePropertyDirectoryTree) + + void handle_moveAudioFile(int replyTo, json_reader *pReader) + { + MoveAudioFileArgs args; + pReader->read(&args); + this->model.MoveAudioFile(args.path_, args.from_, args.to_); + bool result = true; + this->Reply(replyTo, "moveAudioFile", result); + } + REGISTER_MESSAGE_HANDLER(moveAudioFile) + + void handle_setOnboarding(int replyTo, json_reader *pReader) + { + bool value; + pReader->read(&value); + this->model.SetOnboarding(value); + } + REGISTER_MESSAGE_HANDLER(setOnboarding) + + void handle_getWifiRegulatoryDomains(int replyTo, json_reader *pReader) + { + auto regulatoryDomains = this->model.GetWifiRegulatoryDomains(); + this->Reply(replyTo, "getWifiRegulatoryDomains", regulatoryDomains); + } + REGISTER_MESSAGE_HANDLER(getWifiRegulatoryDomains) + + void handle_setAlsaSequencerConfiguration(int replyTo, json_reader *pReader) + { + AlsaSequencerConfiguration config; + pReader->read(&config); + this->model.SetAlsaSequencerConfiguration(config); + this->Reply(replyTo, "setAlsaSequencerConfiguration"); + } + REGISTER_MESSAGE_HANDLER(setAlsaSequencerConfiguration) + + void handle_getAlsaSequencerConfiguration(int replyTo, json_reader *pReader) + { + AlsaSequencerConfiguration config = this->model.GetAlsaSequencerConfiguration(); + this->Reply(replyTo, "getAlsaSequencerConfiguration", config); + } + REGISTER_MESSAGE_HANDLER(getAlsaSequencerConfiguration) + + void handle_getAlsaSequencerPorts(int replyTo, json_reader *pReader) + { + std::vector result = model.GetAlsaSequencerPorts(); + this->Reply(replyTo, "getAlsaSequencerPorts", result); + } + REGISTER_MESSAGE_HANDLER(getAlsaSequencerPorts) + + void handle_requestBankPresets(int replyTo, json_reader *pReader) + { + RequestBankPresetsBody args; + pReader->read(&args); + auto result = this->model.RequestBankPresets(args.bankInstanceId_); + this->Reply(replyTo, "requestBankPresets", result); + } + REGISTER_MESSAGE_HANDLER(requestBankPresets) + + void handle_importPresetsFromBank(int replyTo, json_reader *pReader) + { + ImportPresetsFromBankBody args; + pReader->read(&args); + auto result = this->model.ImportPresetsFromBank(args.bankInstanceId_, args.presets_); + this->Reply(replyTo, "importPresetsFromBank", result); + } + REGISTER_MESSAGE_HANDLER(importPresetsFromBank) + + void handle_copyPresetsToBank(int replyTo, json_reader *pReader) + { + CopyPresetsToBankBody args; + pReader->read(&args); + auto result = this->model.CopyPresetsToBank(args.bankInstanceId_, args.presets_); + this->Reply(replyTo, "copyPresetsToBank", result); + } + REGISTER_MESSAGE_HANDLER(copyPresetsToBank) + + void handle_getChannelRouterSettings(int replyTo, json_reader *pReader) + { + ChannelRouterSettings::ptr result = this->model.GetChannelRouterSettings(); + this->Reply(replyTo, "getChannelRouterSettings", result); + } + REGISTER_MESSAGE_HANDLER(getChannelRouterSettings) + + void handle_setChannelRouterSettings(int replyTo, json_reader *pReader) + { + ChannelRouterSettings::ptr args; + pReader->read(&args); + this->model.SetChannelRouterSettings(this->clientId, args); + } + REGISTER_MESSAGE_HANDLER(setChannelRouterSettings) + + void handle_DownloadModelsFromTone3000(int replyTo, json_reader *pReader) + { + + DownloadModelsFromTone3000Body body; + pReader->read(&body); + auto result = this->model.DownloadModelsFromTone3000( + body.responseUri_, + body.tone3000PckceParams_, + body.downloadPath_, + body.downloadType()); + + this->Reply(replyTo, "downloadModelsFromTone3000", result); + } + REGISTER_MESSAGE_HANDLER(DownloadModelsFromTone3000) + + void handle_cancelTone3000Download(int replyTo, json_reader *pReader) + { + int64_t handle = -1; + pReader->read(&handle); + model.CancelTone3000Download(clientId, handle); + } + REGISTER_MESSAGE_HANDLER(cancelTone3000Download) + + void handle_pingTone3000Server(int replyTo, json_reader *pReader) + { + std::shared_ptr this_ = shared_from_this(); + model.Post([this_, replyTo]() + { + bool result = this_->PingTone3000Server(); + this_->Reply(replyTo,"pingTone3000Server",result); }); + } + REGISTER_MESSAGE_HANDLER(pingTone3000Server) + + void handleMessage(int reply, int replyTo, const std::string &message, json_reader *pReader) + { + if (reply != -1) + { + IRequestReservation *reservation = nullptr; + { + std::lock_guard guard(this->requestMutex); + for (auto i = this->requestReservations.begin(); i != this->requestReservations.end(); ++i) + { + if ((*i)->GetReservationid() == reply) + { + reservation = (*i); + requestReservations.erase(i); + break; + } + } + } + // be careful not to take down the whole server because of a client that's shutting down. + if (reservation != nullptr) + { + try + { + reservation->onResult(pReader); + } + catch (const std::exception &e) + { + Lv2Log::error("Socket: Invalid reply '%s'. (%s)", message.c_str(), e.what()); + } + delete reservation; + } + else + { + Lv2Log::warning("Socket: Reply '%s' with nobody waiting.", message.c_str()); + } + return; + } + if (closed) + { + this->SendError(replyTo, "Server has shut down."); + } + + auto ffHandler = socket_messageHandlers.find(message); + if (ffHandler != socket_messageHandlers.end()) + { + (this->*(ffHandler->second))(replyTo, pReader); + return; + } + Lv2Log::error("Unknown message received: %s", message.c_str()); + SendError(replyTo, std::string("Unknown message: ") + message); + } + +protected: + virtual void + onSocketClosed() override + { + SocketHandler::OnSocketClosed(); + this->Close(); + } + virtual void onReceive(const std::string_view &text) + { + view_istream s(text); + + json_reader reader(s); + // read top level object until we have message + int64_t replyTo = -1; + int64_t reply = -1; + std::string message; + + try + { + reader.consume('['); + reader.consume('{'); + + while (true) + { + if (reader.peek() == '}') + { + reader.consume('}'); + break; + } + std::string name = reader.read_string(); + reader.consume(':'); + if (name == "reply") + { + reader.read(&reply); + } + if (name == "replyTo") + { + reader.read(&replyTo); + } + if (name == "message") + { + message = reader.read_string(); + } + if (reader.peek() == ',') + { + reader.consume(','); + continue; + } + } + if (reader.peek() == ',') + { + reader.consume(','); + handleMessage(reply, replyTo, message, &reader); + } + else + { + handleMessage(reply, replyTo, message, nullptr); + } + // Handle message. + } + catch (const PiPedalException &e) + { + SendError(replyTo, e.what()); + } + catch (const std::exception &e) + { + SendError(replyTo, e.what()); + } + } + +private: + virtual void OnUpdateStatusChanged(const UpdateStatus &updateStatus) + { + Send("onUpdateStatusChanged", updateStatus); + } + + virtual void OnLv2StateChanged(int64_t instanceId, const Lv2PluginState &state) + { + Lv2StateChangedBody message{(uint64_t)instanceId, state}; + Send("onLv2StateChanged", message); + } + + virtual void OnLv2PluginsChanging() override + { + Send("onLv2PluginsChanging", true); + Flush(); + } + virtual void OnHasWifiChanged(bool hasWifi) + { + Send("onHasWifiChanged", hasWifi); + Flush(); + } + + virtual void OnAlsaSequencerConfigurationChanged(const AlsaSequencerConfiguration &alsaSequencerConfiguration) override + { + Send("onAlsaSequencerConfigurationChanged", alsaSequencerConfiguration); + } + + virtual void OnNetworkChanging(bool hotspotConnected) override + { + try + { + Send("onNetworkChanging", hotspotConnected); + Flush(); + } + catch (const std::exception &ignored) + { + } + } + + virtual void OnErrorMessage(const std::string &message) + { + Send("onErrorMessage", message); + } + + virtual void OnTone3000DownloadStarted(int64_t handle, const std::string &title) override + { + Tone3000DownloadStartedBody body; + body.handle_ = handle; + body.title_ = title; + Send("onTone3000DownloadStarted", body); + } + + virtual void OnTone3000DownloadProgress(const Tone3000DownloadProgress &progress) override + { + Send("onTone3000DownloadProgress", progress); + } + + virtual void OnTone3000DownloadComplete(int64_t handle, const std::string &resultPath) override + { + Send("onTone3000DownloadComplete", resultPath); + } + + virtual void OnTone3000DownloadError(int64_t handle, const std::string &errorMessage) override + { + Tone3000DownloadErrorBody body; + body.handle_ = handle; + body.errorMessage_ = errorMessage; + Send("onTone3000DownloadError", body); + } + + // virtual void OnPatchPropertyChanged(int64_t clientId, int64_t instanceId,const std::string& propertyUri,const json_variant& value) + // { + // PatchPropertyChangedBody body; + // body.clientId_ = clientId; + // body.instanceId_ = instanceId; + // body.propertyUri_ = propertyUri; + // body.value_ = value; + // Send("onPatchPropertyChanged",body); + // } + + virtual void OnSystemMidiBindingsChanged(const std::vector &bindings) + { + Send("onSystemMidiBindingsChanged", bindings); + } + + virtual void OnFavoritesChanged(const std::map &favorites) + { + Send("onFavoritesChanged", favorites); + } + + virtual void OnShowStatusMonitorChanged(bool show) + { + Send("onShowStatusMonitorChanged", show); + } + + virtual void OnChannelRouterSettingsChanged(int64_t clientId, const ChannelRouterSettings &channelRouterSettings) + { + ChannelRouterSettingsChangedBody body(clientId, channelRouterSettings); + Send("onChannelSelectionChanged", body); + } + + virtual void OnSnapshotModified(int64_t snapshotIndex, bool modified) + { + SnapshotModifiedBody body; + body.snapshotIndex_ = snapshotIndex; + body.modified_ = modified; + Send("onSnapshotModified", body); + } + + virtual void OnSelectedSnapshotChanged(int64_t selectedSnapshot) override + { + Send("onSelectedSnapshotChanged", selectedSnapshot); + } + + virtual void OnPresetChanged(bool changed) override + { + Send("onPresetChanged", changed); + } + + virtual void OnPresetsChanged(int64_t clientId, const PresetIndex &presets) + { + PresetsChangedBody body; + body.clientId_ = clientId; + body.presets_ = const_cast(&presets); + Send("onPresetsChanged", body); + } + virtual void OnPluginPresetsChanged(const std::string &pluginUri) + { + Send("onPluginPresetsChanged", pluginUri); + } + virtual void OnJackConfigurationChanged(const JackConfiguration &jackConfiguration) + { + Send("onJackConfigurationChanged", jackConfiguration); + } + + virtual void OnLoadPluginPreset(int64_t instanceId, const std::vector &controlValues) + { + OnLoadPluginPresetBody body; + body.instanceId_ = instanceId; + body.controlValues_ = const_cast &>(controlValues); + Send("onLoadPluginPreset", body); + } + + int updateRequestOutstanding = 0; + bool vuUpdateDropped = false; + + virtual void OnVuMeterUpdate(const std::vector &updates) + { + std::lock_guard guard(subscriptionMutex); + if (updateRequestOutstanding < 1) // throttle to accomodate a web page that can't keep up. + { + vuUpdateDropped = false; + for (int i = 0; i < updates.size(); ++i) + { + const VuUpdateX &vuUpdate = updates[i]; + bool interested = false; + for (int i = 0; i < this->activeVuSubscriptions.size(); ++i) + { + if (activeVuSubscriptions[i].instanceId == vuUpdate.instanceId_) + { + interested = true; + break; + } + } + if (interested) + { + updateRequestOutstanding++; + this->Request( + "onVuUpdate", + vuUpdate, + [this](const bool &result) + { + this->updateRequestOutstanding--; + }, + [this](const std::exception &) + { + this->updateRequestOutstanding--; + }); + } + } + } + else + { + if (!vuUpdateDropped) + { + vuUpdateDropped = true; + Lv2Log::debug("Vu Update(s) dropped."); + } + } + } + + virtual void OnVst3ControlChanged(int64_t clientId, int64_t instanceId, const std::string &key, float value, const std::string &state) + { + Vst3ControlChangedBody body; + body.clientId_ = clientId; + body.instanceId_ = instanceId; + body.symbol_ = key; + body.value_ = value; + body.state_ = state; + Send("onVst3ControlChanged", body); + } + + virtual void OnControlChanged(int64_t clientId, int64_t instanceId, const std::string &key, float value) + { + ControlChangedBody body; + body.clientId_ = clientId; + body.instanceId_ = instanceId; + body.symbol_ = key; + body.value_ = value; + Send("onControlChanged", body); + } + virtual void OnInputVolumeChanged(float value) + { + ControlChangedBody body; + Send("onInputVolumeChanged", value); + } + virtual void OnOutputVolumeChanged(float value) + { + ControlChangedBody body; + Send("onOutputVolumeChanged", value); + } + + class DeferredValue + { + public: + int64_t instanceId; + std::string symbol; + float value; + }; + + std::vector deferredValues; + bool midiValueChangedOutstanding = false; + + virtual void OnMidiValueChanged(int64_t instanceId, const std::string &symbol, float value) + { + if (midiValueChangedOutstanding) + { + for (size_t i = 0; i < deferredValues.size(); ++i) + { + auto &deferredValue = deferredValues[i]; + if (deferredValue.instanceId == instanceId && deferredValue.symbol == symbol) + { + deferredValue.value = value; + return; + } + } + DeferredValue newValue{instanceId, symbol, value}; + deferredValues.push_back(newValue); + } + else + { + midiValueChangedOutstanding = true; + ControlChangedBody body; + body.clientId_ = -1; + body.instanceId_ = instanceId; + body.symbol_ = symbol; + body.value_ = value; + Request( + "onMidiValueChanged", body, + [this](const bool &value) + { + this->midiValueChangedOutstanding = false; + if (this->deferredValues.size() != 0) + { + DeferredValue value = deferredValues[0]; + deferredValues.erase(deferredValues.begin()); + this->OnMidiValueChanged(value.instanceId, value.symbol, value.value); + } + }, + [](const std::exception &e) { + + }); + } + } + + void Flush() + { + } + int outstandingNotifyAtomOutputs = 0; + + class PendingNotifyAtomOutput + { + public: + int64_t clientHandle; + uint64_t instanceId; + std::string propertyUri; + std::string json; + }; + + std::vector pendingNotifyAtomOutputs; + + void OnAckNotifyPatchProperty() + { + std::lock_guard guard(subscriptionMutex); + if (--outstandingNotifyAtomOutputs <= 0) + { + outstandingNotifyAtomOutputs = 0; + if (pendingNotifyAtomOutputs.size() != 0) + { + PendingNotifyAtomOutput t = pendingNotifyAtomOutputs[0]; + pendingNotifyAtomOutputs.erase(pendingNotifyAtomOutputs.begin()); + OnNotifyPatchProperty( + t.clientHandle, + t.instanceId, + t.propertyUri, + t.json); + } + } + } + + virtual void OnNotifyPatchProperty(int64_t clientHandle, uint64_t instanceId, const std::string &atomProperty, const std::string &atomJson) + { + NotifyAtomOutputBody body; + body.clientHandle_ = clientHandle; + body.instanceId_ = instanceId; + body.propertyUri_ = atomProperty; + body.atomJson_.Set(atomJson); + + // flow control. We can only have one in-flight NotifyAtomOutput at a time. + // Subsequent notifications are held until we receive an ack. + // + // If a duplicate atomProperty is queued, overwrite the previous value. + { + std::lock_guard guard(subscriptionMutex); + + if (outstandingNotifyAtomOutputs != 0) + { + for (size_t i = 0; i < pendingNotifyAtomOutputs.size(); ++i) + { + auto &output = pendingNotifyAtomOutputs[i]; + if (output.clientHandle == clientHandle && output.instanceId == instanceId && output.propertyUri == atomProperty) + { + // better to erase than overwrite, since it provides better idempotence. + pendingNotifyAtomOutputs.erase(pendingNotifyAtomOutputs.begin() + i); + break; + } + } + pendingNotifyAtomOutputs.push_back(PendingNotifyAtomOutput{clientHandle, instanceId, atomProperty, atomJson}); + return; + } + ++outstandingNotifyAtomOutputs; + } + + Request( + "onNotifyPatchProperty", body, + [this](const bool &value) + { + this->OnAckNotifyPatchProperty(); + }, + [](const std::exception &e) { + + }); + } + virtual void OnNotifyPathPatchPropertyChanged(int64_t instanceId, const std::string &pathPatchPropertyString, const std::string &atomString) + { + PathPatchPropertyChangedBody body; + body.instanceId_ = instanceId; + body.propertyUri_ = pathPatchPropertyString; + body.atomJson_ = atomString; + + Send("onNotifyPathPatchPropertyChanged", body); + } + + virtual void OnNotifyMidiListener(int64_t clientHandle, uint8_t cc0, uint8_t cc1, uint8_t cc2) override + { + NotifyMidiListenerBody body; + body.clientHandle_ = clientHandle; + body.cc0_ = cc0; + body.cc1_ = cc1; + body.cc2_ = cc2; + + Send("onNotifyMidiListener", body); + } + + virtual void OnBankIndexChanged(const BankIndex &bankIndex) + { + Send("onBanksChanged", bankIndex); + } + + virtual void OnJackServerSettingsChanged(const JackServerSettings &jackServerSettings) + { + Send("onJackServerSettingsChanged", jackServerSettings); + } + virtual void OnWifiConfigSettingsChanged(const WifiConfigSettings &wifiConfigSettings) + { + Send("onWifiConfigSettingsChanged", wifiConfigSettings); + } + + virtual void OnWifiDirectConfigSettingsChanged(const WifiDirectConfigSettings &wifiConfigSettings) + { + Send("onWifiDirectConfigSettingsChanged", wifiConfigSettings); + } + virtual void OnGovernorSettingsChanged(const std::string &governor) + { + Send("onGovernorSettingsChanged", governor); + } + + virtual void OnPedalboardChanged(int64_t clientId, const Pedalboard &pedalboard) + { + UpdateCurrentPedalboardBody body; + body.clientId_ = clientId; + body.pedalboard_ = pedalboard; + Send("onPedalboardChanged", body); + } + + virtual void OnItemEnabledChanged(int64_t clientId, int64_t pedalItemId, bool enabled) + { + PedalboardItemEnabledBody body; + body.clientId_ = clientId; + body.instanceId_ = pedalItemId; + body.enabled_ = enabled; + Send("onItemEnabledChanged", body); + } + virtual void OnItemUseModUiChanged(int64_t clientId, int64_t pedalItemId, bool enabled) + { + PedalboardItemEnabledBody body; + body.clientId_ = clientId; + body.instanceId_ = pedalItemId; + body.enabled_ = enabled; + Send("onUseItemModUiChanged", body); + } +}; + +std::atomic PiPedalSocketHandler::nextClientId = 0; + +class PiPedalSocketFactory : public ISocketFactory +{ +private: + PiPedalModel &model; + +public: + virtual ~PiPedalSocketFactory() + { + } + PiPedalSocketFactory(PiPedalModel &model) + : model(model) + { + } + +public: + virtual bool wants(const uri &request) + { + if (request.segment(0) == "pipedal") + return true; + return false; + } + virtual std::shared_ptr CreateHandler(const uri &request) + { + return std::shared_ptr(new PiPedalSocketHandler(model)); + } +}; + +std::shared_ptr pipedal::MakePiPedalSocketFactory(PiPedalModel &model) +{ + return std::make_shared(model); +} + +bool PiPedalSocketHandler::PingTone3000Server() +{ + constexpr const char *TONE3000_PING_URL = "https://www.tone3000.com/robots.txt"; + std::vector output; + std::vector headers; + try + { + int result = CurlGet(TONE3000_PING_URL, output, &headers); + return result == 200; + } + catch (const std::exception &e) + { + Lv2Log::error("PingTone3000Server: %s", e.what()); + return false; + } +} \ No newline at end of file