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

This commit is contained in:
Aiden
2026-05-11 00:26:59 +10:00
parent f6b26bf28b
commit 27dbb55f7b
10 changed files with 106 additions and 214 deletions

View File

@@ -4,6 +4,7 @@
#include "OscServer.h"
#include "RuntimeControlBridge.h"
#include "RuntimeHost.h"
#include "RuntimeStore.h"
#include <windows.h>
ControlServices::ControlServices() :
@@ -34,9 +35,9 @@ bool ControlServices::Start(OpenGLComposite& composite, RuntimeHost& runtimeHost
return true;
}
void ControlServices::BeginPolling(RuntimeHost& runtimeHost)
void ControlServices::BeginPolling(RuntimeHost& runtimeHost, RuntimeStore& runtimeStore)
{
StartPolling(runtimeHost);
StartPolling(runtimeHost, runtimeStore);
}
void ControlServices::Stop()
@@ -174,12 +175,12 @@ RuntimePollEvents ControlServices::ConsumePollEvents()
return events;
}
void ControlServices::StartPolling(RuntimeHost& runtimeHost)
void ControlServices::StartPolling(RuntimeHost& runtimeHost, RuntimeStore& runtimeStore)
{
if (mPollRunning.exchange(true))
return;
mPollThread = std::thread([this, &runtimeHost]() { PollLoop(runtimeHost); });
mPollThread = std::thread([this, &runtimeHost, &runtimeStore]() { PollLoop(runtimeHost, runtimeStore); });
}
void ControlServices::StopPolling()
@@ -191,7 +192,7 @@ void ControlServices::StopPolling()
mPollThread.join();
}
void ControlServices::PollLoop(RuntimeHost& runtimeHost)
void ControlServices::PollLoop(RuntimeHost& runtimeHost, RuntimeStore& runtimeStore)
{
while (mPollRunning)
{
@@ -203,7 +204,7 @@ void ControlServices::PollLoop(RuntimeHost& runtimeHost)
for (const auto& entry : pendingCommits)
{
std::string commitError;
if (runtimeHost.UpdateLayerParameterByControlKey(
if (runtimeStore.SetStoredParameterValueByControlKey(
entry.second.layerKey,
entry.second.parameterKey,
entry.second.value,

View File

@@ -15,6 +15,7 @@ class ControlServer;
class OpenGLComposite;
class OscServer;
class RuntimeHost;
class RuntimeStore;
struct RuntimePollEvents
{
@@ -45,7 +46,7 @@ public:
~ControlServices();
bool Start(OpenGLComposite& composite, RuntimeHost& runtimeHost, std::string& error);
void BeginPolling(RuntimeHost& runtimeHost);
void BeginPolling(RuntimeHost& runtimeHost, RuntimeStore& runtimeStore);
void Stop();
void BroadcastState();
void RequestBroadcastState();
@@ -73,9 +74,9 @@ private:
uint64_t generation = 0;
};
void StartPolling(RuntimeHost& runtimeHost);
void StartPolling(RuntimeHost& runtimeHost, RuntimeStore& runtimeStore);
void StopPolling();
void PollLoop(RuntimeHost& runtimeHost);
void PollLoop(RuntimeHost& runtimeHost, RuntimeStore& runtimeStore);
std::unique_ptr<ControlServer> mControlServer;
std::unique_ptr<OscServer> mOscServer;

View File

@@ -1,5 +1,7 @@
#include "RuntimeServices.h"
#include "RuntimeStore.h"
RuntimeServices::RuntimeServices() :
mControlServices(std::make_unique<ControlServices>())
{
@@ -15,10 +17,10 @@ bool RuntimeServices::Start(OpenGLComposite& composite, RuntimeHost& runtimeHost
return mControlServices && mControlServices->Start(composite, runtimeHost, error);
}
void RuntimeServices::BeginPolling(RuntimeHost& runtimeHost)
void RuntimeServices::BeginPolling(RuntimeHost& runtimeHost, RuntimeStore& runtimeStore)
{
if (mControlServices)
mControlServices->BeginPolling(runtimeHost);
mControlServices->BeginPolling(runtimeHost, runtimeStore);
}
void RuntimeServices::Stop()

View File

@@ -6,6 +6,7 @@
#include <string>
class OpenGLComposite;
class RuntimeHost;
class RuntimeStore;
class RuntimeServices
{
@@ -17,7 +18,7 @@ public:
~RuntimeServices();
bool Start(OpenGLComposite& composite, RuntimeHost& runtimeHost, std::string& error);
void BeginPolling(RuntimeHost& runtimeHost);
void BeginPolling(RuntimeHost& runtimeHost, RuntimeStore& runtimeStore);
void Stop();
void BroadcastState();
void RequestBroadcastState();

View File

@@ -270,7 +270,7 @@ bool OpenGLComposite::InitOpenGLState()
mRenderEngine->ResetShaderFeedbackState();
broadcastRuntimeState();
mRuntimeServices->BeginPolling(*mRuntimeHost);
mRuntimeServices->BeginPolling(*mRuntimeHost, *mRuntimeStore);
return true;
}

View File

@@ -825,201 +825,6 @@ void RuntimeHost::ClearReloadRequest()
mReloadRequested = false;
}
bool RuntimeHost::UpdateLayerParameterByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue, std::string& error)
{
return UpdateLayerParameterByControlKey(layerKey, parameterKey, newValue, true, error);
}
bool RuntimeHost::UpdateLayerParameterByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue, bool persistState, std::string& error)
{
std::lock_guard<std::mutex> lock(mMutex);
LayerPersistentState* matchedLayer = nullptr;
const ShaderPackage* matchedPackage = nullptr;
for (LayerPersistentState& layer : mPersistentState.layers)
{
auto shaderIt = mPackagesById.find(layer.shaderId);
if (shaderIt == mPackagesById.end())
continue;
if (MatchesControlKey(layer.id, layerKey) || MatchesControlKey(shaderIt->second.id, layerKey) ||
MatchesControlKey(shaderIt->second.displayName, layerKey))
{
matchedLayer = &layer;
matchedPackage = &shaderIt->second;
break;
}
}
if (!matchedLayer || !matchedPackage)
{
error = "Unknown OSC layer key: " + layerKey;
return false;
}
const auto parameterIt = std::find_if(matchedPackage->parameters.begin(), matchedPackage->parameters.end(),
[&parameterKey](const ShaderParameterDefinition& definition)
{
return MatchesControlKey(definition.id, parameterKey) || MatchesControlKey(definition.label, parameterKey);
});
if (parameterIt == matchedPackage->parameters.end())
{
error = "Unknown OSC parameter key: " + parameterKey;
return false;
}
if (parameterIt->type == ShaderParameterType::Trigger)
{
ShaderParameterValue& value = matchedLayer->parameterValues[parameterIt->id];
const double previousCount = value.numberValues.empty() ? 0.0 : value.numberValues[0];
const double triggerTime = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - mStartTime).count();
value.numberValues = { previousCount + 1.0, triggerTime };
MarkParameterStateDirtyLocked();
return true;
}
ShaderParameterValue normalized;
if (!NormalizeAndValidateValue(*parameterIt, newValue, normalized, error))
return false;
matchedLayer->parameterValues[parameterIt->id] = normalized;
MarkParameterStateDirtyLocked();
return !persistState || SavePersistentState(error);
}
bool RuntimeHost::ApplyOscTargetByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& targetValue, double smoothingAmount, bool& keepApplying, std::string& resolvedLayerId, std::string& resolvedParameterId, ShaderParameterValue& appliedValue, std::string& error)
{
keepApplying = false;
resolvedLayerId.clear();
resolvedParameterId.clear();
appliedValue = ShaderParameterValue();
std::lock_guard<std::mutex> lock(mMutex);
LayerPersistentState* matchedLayer = nullptr;
const ShaderPackage* matchedPackage = nullptr;
for (LayerPersistentState& layer : mPersistentState.layers)
{
auto shaderIt = mPackagesById.find(layer.shaderId);
if (shaderIt == mPackagesById.end())
continue;
if (MatchesControlKey(layer.id, layerKey) || MatchesControlKey(shaderIt->second.id, layerKey) ||
MatchesControlKey(shaderIt->second.displayName, layerKey))
{
matchedLayer = &layer;
matchedPackage = &shaderIt->second;
break;
}
}
if (!matchedLayer || !matchedPackage)
{
error = "Unknown OSC layer key: " + layerKey;
return false;
}
resolvedLayerId = matchedLayer->id;
const auto parameterIt = std::find_if(matchedPackage->parameters.begin(), matchedPackage->parameters.end(),
[&parameterKey](const ShaderParameterDefinition& definition)
{
return MatchesControlKey(definition.id, parameterKey) || MatchesControlKey(definition.label, parameterKey);
});
if (parameterIt == matchedPackage->parameters.end())
{
error = "Unknown OSC parameter key: " + parameterKey;
return false;
}
resolvedParameterId = parameterIt->id;
if (parameterIt->type == ShaderParameterType::Trigger)
{
ShaderParameterValue& value = matchedLayer->parameterValues[parameterIt->id];
const double previousCount = value.numberValues.empty() ? 0.0 : value.numberValues[0];
const double triggerTime = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - mStartTime).count();
value.numberValues = { previousCount + 1.0, triggerTime };
MarkParameterStateDirtyLocked();
appliedValue = value;
return true;
}
ShaderParameterValue normalizedTarget;
if (!NormalizeAndValidateValue(*parameterIt, targetValue, normalizedTarget, error))
return false;
const bool smoothableType =
parameterIt->type == ShaderParameterType::Float ||
parameterIt->type == ShaderParameterType::Vec2 ||
parameterIt->type == ShaderParameterType::Color;
const bool smoothableInput = targetValue.isNumber() || JsonValueContainsOnlyNumbers(targetValue);
const double alpha = Clamp01(smoothingAmount);
if (!smoothableType || !smoothableInput || alpha <= 0.0)
{
matchedLayer->parameterValues[parameterIt->id] = normalizedTarget;
MarkParameterStateDirtyLocked();
appliedValue = normalizedTarget;
return true;
}
ShaderParameterValue currentValue = DefaultValueForDefinition(*parameterIt);
auto currentIt = matchedLayer->parameterValues.find(parameterIt->id);
if (currentIt != matchedLayer->parameterValues.end())
currentValue = currentIt->second;
ShaderParameterValue nextValue = normalizedTarget;
nextValue.numberValues = normalizedTarget.numberValues;
if (currentValue.numberValues.size() != normalizedTarget.numberValues.size())
currentValue.numberValues = normalizedTarget.numberValues;
bool changed = false;
bool converged = true;
for (std::size_t index = 0; index < normalizedTarget.numberValues.size(); ++index)
{
const double currentNumber = currentValue.numberValues[index];
const double targetNumber = normalizedTarget.numberValues[index];
const double delta = targetNumber - currentNumber;
double nextNumber = currentNumber + delta * alpha;
if (std::fabs(delta) <= 0.0005)
{
nextNumber = targetNumber;
}
else
{
converged = false;
}
if (std::fabs(nextNumber - currentNumber) > 0.0000001)
changed = true;
nextValue.numberValues[index] = nextNumber;
}
if (!converged)
{
keepApplying = true;
}
else
{
nextValue.numberValues = normalizedTarget.numberValues;
}
if (!changed && !keepApplying)
{
appliedValue = matchedLayer->parameterValues[parameterIt->id];
return true;
}
matchedLayer->parameterValues[parameterIt->id] = nextValue;
MarkParameterStateDirtyLocked();
appliedValue = nextValue;
return true;
}
void RuntimeHost::SetCompileStatus(bool succeeded, const std::string& message)
{
std::lock_guard<std::mutex> lock(mMutex);

View File

@@ -25,10 +25,6 @@ public:
bool ManualReloadRequested();
void ClearReloadRequest();
bool UpdateLayerParameterByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue, std::string& error);
bool UpdateLayerParameterByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue, bool persistState, std::string& error);
bool ApplyOscTargetByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& targetValue, double smoothingAmount, bool& keepApplying, std::string& resolvedLayerId, std::string& resolvedParameterId, ShaderParameterValue& appliedValue, std::string& error);
void SetCompileStatus(bool succeeded, const std::string& message);
void SetSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName);
bool TrySetSignalStatus(bool hasSignal, unsigned width, unsigned height, const std::string& modeName);

View File

@@ -14,6 +14,22 @@ std::string TrimCopy(const std::string& text)
return text.substr(start, end - start);
}
std::string SimplifyControlKey(const std::string& text)
{
std::string simplified;
for (unsigned char ch : text)
{
if (std::isalnum(ch))
simplified.push_back(static_cast<char>(std::tolower(ch)));
}
return simplified;
}
bool MatchesControlKey(const std::string& candidate, const std::string& key)
{
return candidate == key || SimplifyControlKey(candidate) == SimplifyControlKey(key);
}
}
RuntimeStore::RuntimeStore(RuntimeHost& runtimeHost) :
@@ -256,12 +272,36 @@ bool RuntimeStore::SetStoredParameterValue(const std::string& layerId, const std
bool RuntimeStore::SetStoredParameterValueByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue, std::string& error)
{
return mRuntimeHost.UpdateLayerParameterByControlKey(layerKey, parameterKey, newValue, error);
return SetStoredParameterValueByControlKey(layerKey, parameterKey, newValue, true, error);
}
bool RuntimeStore::SetStoredParameterValueByControlKey(const std::string& layerKey, const std::string& parameterKey, const JsonValue& newValue, bool persistState, std::string& error)
{
return mRuntimeHost.UpdateLayerParameterByControlKey(layerKey, parameterKey, newValue, persistState, error);
std::lock_guard<std::mutex> lock(mRuntimeHost.mMutex);
RuntimeHost::LayerPersistentState* matchedLayer = nullptr;
const ShaderPackage* matchedPackage = nullptr;
std::vector<ShaderParameterDefinition>::const_iterator parameterIt;
if (!TryResolveStoredLayerAndParameterByControlKeyLocked(layerKey, parameterKey, matchedLayer, matchedPackage, parameterIt, error))
return false;
if (parameterIt->type == ShaderParameterType::Trigger)
{
ShaderParameterValue& value = matchedLayer->parameterValues[parameterIt->id];
const double previousCount = value.numberValues.empty() ? 0.0 : value.numberValues[0];
const double triggerTime = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - mRuntimeHost.mStartTime).count();
value.numberValues = { previousCount + 1.0, triggerTime };
mRuntimeHost.MarkParameterStateDirtyLocked();
return true;
}
ShaderParameterValue normalized;
if (!mRuntimeHost.NormalizeAndValidateValue(*parameterIt, newValue, normalized, error))
return false;
matchedLayer->parameterValues[parameterIt->id] = normalized;
mRuntimeHost.MarkParameterStateDirtyLocked();
return !persistState || mRuntimeHost.SavePersistentState(error);
}
bool RuntimeStore::ResetStoredLayerParameterValues(const std::string& layerId, std::string& error)
@@ -551,3 +591,45 @@ JsonValue RuntimeStore::SerializeLayerStack() const
{
return mRuntimeHost.SerializeLayerStackLocked();
}
bool RuntimeStore::TryResolveStoredLayerAndParameterByControlKeyLocked(const std::string& layerKey, const std::string& parameterKey,
RuntimeHost::LayerPersistentState*& matchedLayer, const ShaderPackage*& matchedPackage,
std::vector<ShaderParameterDefinition>::const_iterator& parameterIt, std::string& error) const
{
matchedLayer = nullptr;
matchedPackage = nullptr;
for (RuntimeHost::LayerPersistentState& layer : mRuntimeHost.mPersistentState.layers)
{
auto shaderIt = mRuntimeHost.mPackagesById.find(layer.shaderId);
if (shaderIt == mRuntimeHost.mPackagesById.end())
continue;
if (MatchesControlKey(layer.id, layerKey) || MatchesControlKey(shaderIt->second.id, layerKey) ||
MatchesControlKey(shaderIt->second.displayName, layerKey))
{
matchedLayer = &layer;
matchedPackage = &shaderIt->second;
break;
}
}
if (!matchedLayer || !matchedPackage)
{
error = "Unknown OSC layer key: " + layerKey;
return false;
}
parameterIt = std::find_if(matchedPackage->parameters.begin(), matchedPackage->parameters.end(),
[&parameterKey](const ShaderParameterDefinition& definition)
{
return MatchesControlKey(definition.id, parameterKey) || MatchesControlKey(definition.label, parameterKey);
});
if (parameterIt == matchedPackage->parameters.end())
{
error = "Unknown OSC parameter key: " + parameterKey;
return false;
}
return true;
}

View File

@@ -46,6 +46,9 @@ public:
void ClearReloadRequest();
private:
bool TryResolveStoredLayerAndParameterByControlKeyLocked(const std::string& layerKey, const std::string& parameterKey,
RuntimeHost::LayerPersistentState*& matchedLayer, const ShaderPackage*& matchedPackage,
std::vector<ShaderParameterDefinition>::const_iterator& parameterIt, std::string& error) const;
JsonValue BuildRuntimeStateValue() const;
JsonValue SerializeLayerStack() const;