// 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 "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 "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 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; 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) { 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_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; } }