OSC fixes
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 2m22s
CI / Windows Release Package (push) Successful in 2m43s

This commit is contained in:
Aiden
2026-05-10 18:37:30 +10:00
parent f11d531e0c
commit d7ca42b51b
14 changed files with 549 additions and 16 deletions

View File

@@ -17,6 +17,7 @@
namespace
{
constexpr DWORD kStateBroadcastIntervalMs = 250;
constexpr DWORD kStateBroadcastThrottleMs = 50;
bool InitializeWinsock(std::string& error)
{
@@ -75,7 +76,7 @@ std::string GuessContentType(const std::filesystem::path& assetPath)
}
ControlServer::ControlServer()
: mPort(0), mRunning(false)
: mPort(0), mRunning(false), mBroadcastPending(false)
{
}
@@ -161,10 +162,16 @@ void ControlServer::Stop()
void ControlServer::BroadcastState()
{
mBroadcastPending = false;
std::lock_guard<std::mutex> lock(mMutex);
BroadcastStateLocked();
}
void ControlServer::RequestBroadcastState()
{
mBroadcastPending = true;
}
void ControlServer::ServerLoop()
{
DWORD lastStateBroadcastMs = GetTickCount();
@@ -173,7 +180,12 @@ void ControlServer::ServerLoop()
TryAcceptClient();
const DWORD nowMs = GetTickCount();
if (nowMs - lastStateBroadcastMs >= kStateBroadcastIntervalMs)
if (mBroadcastPending && nowMs - lastStateBroadcastMs >= kStateBroadcastThrottleMs)
{
BroadcastState();
lastStateBroadcastMs = nowMs;
}
else if (nowMs - lastStateBroadcastMs >= kStateBroadcastIntervalMs)
{
BroadcastState();
lastStateBroadcastMs = nowMs;
@@ -469,6 +481,7 @@ bool ControlServer::HandleWebSocketUpgrade(UniqueSocket clientSocket, const Http
client.socket.reset(clientSocket.release());
client.websocket = true;
mClients.push_back(std::move(client));
mBroadcastPending = false;
BroadcastStateLocked();
}
return true;

View File

@@ -41,6 +41,7 @@ public:
bool Start(const std::filesystem::path& uiRoot, const std::filesystem::path& docsRoot, unsigned short preferredPort, const Callbacks& callbacks, std::string& error);
void Stop();
void BroadcastState();
void RequestBroadcastState();
unsigned short GetPort() const { return mPort; }
@@ -100,6 +101,7 @@ private:
unsigned short mPort;
std::thread mThread;
std::atomic<bool> mRunning;
std::atomic<bool> mBroadcastPending;
mutable std::mutex mMutex;
std::vector<ClientConnection> mClients;
};

View File

@@ -4,10 +4,12 @@
#include "OpenGLComposite.h"
#include "OscServer.h"
#include "RuntimeHost.h"
#include "RuntimeServices.h"
bool StartRuntimeControlServices(
OpenGLComposite& composite,
RuntimeHost& runtimeHost,
RuntimeServices& runtimeServices,
ControlServer& controlServer,
OscServer& oscServer,
std::string& error)
@@ -41,8 +43,8 @@ bool StartRuntimeControlServices(
runtimeHost.SetServerPort(controlServer.GetPort());
OscServer::Callbacks oscCallbacks;
oscCallbacks.updateParameter = [&composite](const std::string& layerKey, const std::string& parameterKey, const std::string& valueJson, std::string& actionError) {
return composite.UpdateLayerParameterByControlKeyJson(layerKey, parameterKey, valueJson, actionError);
oscCallbacks.updateParameter = [&runtimeServices](const std::string& layerKey, const std::string& parameterKey, const std::string& valueJson, std::string& actionError) {
return runtimeServices.QueueOscUpdate(layerKey, parameterKey, valueJson, actionError);
};
if (runtimeHost.GetOscPort() > 0 && !oscServer.Start(runtimeHost.GetOscBindAddress(), runtimeHost.GetOscPort(), oscCallbacks, error))
return false;

View File

@@ -6,10 +6,12 @@ class ControlServer;
class OpenGLComposite;
class OscServer;
class RuntimeHost;
class RuntimeServices;
bool StartRuntimeControlServices(
OpenGLComposite& composite,
RuntimeHost& runtimeHost,
RuntimeServices& runtimeServices,
ControlServer& controlServer,
OscServer& oscServer,
std::string& error);

View File

@@ -4,7 +4,6 @@
#include "OscServer.h"
#include "RuntimeControlBridge.h"
#include "RuntimeHost.h"
#include <windows.h>
RuntimeServices::RuntimeServices() :
@@ -26,7 +25,7 @@ bool RuntimeServices::Start(OpenGLComposite& composite, RuntimeHost& runtimeHost
{
Stop();
if (!StartRuntimeControlServices(composite, runtimeHost, *mControlServer, *mOscServer, error))
if (!StartRuntimeControlServices(composite, runtimeHost, *this, *mControlServer, *mOscServer, error))
{
Stop();
return false;
@@ -57,6 +56,62 @@ void RuntimeServices::BroadcastState()
mControlServer->BroadcastState();
}
void RuntimeServices::RequestBroadcastState()
{
if (mControlServer)
mControlServer->RequestBroadcastState();
}
bool RuntimeServices::QueueOscUpdate(const std::string& layerKey, const std::string& parameterKey, const std::string& valueJson, std::string& error)
{
(void)error;
PendingOscUpdate update;
update.layerKey = layerKey;
update.parameterKey = parameterKey;
update.valueJson = valueJson;
const std::string routeKey = layerKey + "\n" + parameterKey;
{
std::lock_guard<std::mutex> lock(mPendingOscMutex);
mPendingOscUpdates[routeKey] = std::move(update);
}
return true;
}
bool RuntimeServices::ApplyPendingOscUpdates(std::vector<AppliedOscUpdate>& appliedUpdates, std::string& error)
{
appliedUpdates.clear();
std::map<std::string, PendingOscUpdate> pending;
{
std::lock_guard<std::mutex> lock(mPendingOscMutex);
if (mPendingOscUpdates.empty())
return true;
pending.swap(mPendingOscUpdates);
}
for (const auto& entry : pending)
{
JsonValue targetValue;
std::string parseError;
if (!ParseJson(entry.second.valueJson, targetValue, parseError))
{
OutputDebugStringA(("OSC queued value parse failed: " + parseError + "\n").c_str());
continue;
}
AppliedOscUpdate appliedUpdate;
appliedUpdate.layerKey = entry.second.layerKey;
appliedUpdate.parameterKey = entry.second.parameterKey;
appliedUpdate.targetValue = targetValue;
appliedUpdates.push_back(std::move(appliedUpdate));
}
(void)error;
return true;
}
RuntimePollEvents RuntimeServices::ConsumePollEvents()
{
RuntimePollEvents events;

View File

@@ -1,6 +1,10 @@
#pragma once
#include "RuntimeJson.h"
#include "ShaderTypes.h"
#include <atomic>
#include <map>
#include <memory>
#include <mutex>
#include <string>
@@ -22,6 +26,13 @@ struct RuntimePollEvents
class RuntimeServices
{
public:
struct AppliedOscUpdate
{
std::string layerKey;
std::string parameterKey;
JsonValue targetValue;
};
RuntimeServices();
~RuntimeServices();
@@ -29,9 +40,19 @@ public:
void BeginPolling(RuntimeHost& runtimeHost);
void Stop();
void BroadcastState();
void RequestBroadcastState();
bool QueueOscUpdate(const std::string& layerKey, const std::string& parameterKey, const std::string& valueJson, std::string& error);
bool ApplyPendingOscUpdates(std::vector<AppliedOscUpdate>& appliedUpdates, std::string& error);
RuntimePollEvents ConsumePollEvents();
private:
struct PendingOscUpdate
{
std::string layerKey;
std::string parameterKey;
std::string valueJson;
};
void StartPolling(RuntimeHost& runtimeHost);
void StopPolling();
void PollLoop(RuntimeHost& runtimeHost);
@@ -45,4 +66,6 @@ private:
std::atomic<bool> mPollFailed;
std::mutex mPollErrorMutex;
std::string mPollError;
std::mutex mPendingOscMutex;
std::map<std::string, PendingOscUpdate> mPendingOscUpdates;
};