This commit is contained in:
2021-10-14 13:47:35 +02:00
commit 6625a8dfaa
4026 changed files with 844291 additions and 0 deletions
@@ -0,0 +1,154 @@
#if defined TARGET_HAS_ThirdPartyVRPN
#include "ovpCBoxAlgorithmVRPNAnalogClient.h"
namespace OpenViBE {
namespace Plugins {
namespace VRPN {
static void VRPN_CALLBACK vrpn_analog_cb(void* data, const vrpn_ANALOGCB a)
{
CBoxAlgorithmVRPNAnalogClient* client = static_cast<CBoxAlgorithmVRPNAnalogClient*>(data);
client->setAnalog(a.num_channel, a.channel);
}
bool CBoxAlgorithmVRPNAnalogClient::initialize()
{
m_vrpnAnalogRemote = nullptr;
m_peripheralName = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
m_sampling = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
m_nChannel = uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2));
m_nSamplePerSentBlock = uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 3));
m_encoder = &this->getAlgorithmManager().getAlgorithm(this->getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SignalEncoder));
m_encoder->initialize();
ip_matrix.initialize(m_encoder->getInputParameter(OVP_GD_Algorithm_SignalEncoder_InputParameterId_Matrix));
ip_sampling.initialize(m_encoder->getInputParameter(OVP_GD_Algorithm_SignalEncoder_InputParameterId_Sampling));
op_buffer.initialize(m_encoder->getOutputParameter(OVP_GD_Algorithm_SignalEncoder_OutputParameterId_EncodedMemoryBuffer));
m_firstStart = true;
m_lastChunkEndTime = 0;
m_chunkDuration = CTime(m_sampling, m_nSamplePerSentBlock).time();
m_lastSamples.resize(m_nChannel);
m_sampleBuffer.clear();
return true;
}
bool CBoxAlgorithmVRPNAnalogClient::uninitialize()
{
if (m_vrpnAnalogRemote)
{
delete m_vrpnAnalogRemote;
m_vrpnAnalogRemote = nullptr;
}
if (m_encoder)
{
m_encoder->uninitialize();
this->getAlgorithmManager().releaseAlgorithm(*m_encoder);
m_encoder = nullptr;
}
m_sampleBuffer.clear();
m_lastSamples.clear();
return true;
}
bool CBoxAlgorithmVRPNAnalogClient::processClock(Kernel::CMessageClock& /*msg*/)
{
this->getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
bool CBoxAlgorithmVRPNAnalogClient::process()
{
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
if (m_firstStart)
{
ip_sampling = m_sampling;
ip_matrix->resize(m_nChannel, m_nSamplePerSentBlock);
// @TODO do labels ?
op_buffer = boxContext.getOutputChunk(0);
m_encoder->process(OVP_GD_Algorithm_SignalEncoder_InputTriggerId_EncodeHeader);
boxContext.markOutputAsReadyToSend(0, 0, 0);
m_lastSamples.resize(m_nChannel);
for (size_t i = 0; i < m_nChannel; ++i) { m_lastSamples[i] = 0; }
m_firstStart = false;
}
if (!m_vrpnAnalogRemote)
{
m_vrpnAnalogRemote = new vrpn_Analog_Remote(m_peripheralName.toASCIIString());
m_vrpnAnalogRemote->register_change_handler(this, &vrpn_analog_cb);
}
m_vrpnAnalogRemote->mainloop();
const uint64_t time = this->getPlayerContext().getCurrentTime();
if ((time - m_lastChunkEndTime) >= m_chunkDuration)
{
// Time to send a chunk. Copy our current sample buffer to the output matrix.
size_t idx = 0;
double* oBuffer = ip_matrix->getBuffer();
while (!m_sampleBuffer.empty())
{
const std::vector<double>& tmp = m_sampleBuffer.front();
for (size_t j = 0; j < m_nChannel; ++j) { oBuffer[j * m_nSamplePerSentBlock + idx] = tmp[j]; }
m_sampleBuffer.pop_front();
idx++;
}
// If the buffer didn't have enough data from callbacks, pad with the last sample
while (idx < m_nSamplePerSentBlock)
{
for (size_t j = 0; j < m_nChannel; ++j) { oBuffer[j * m_nSamplePerSentBlock + idx] = m_lastSamples[j]; }
idx++;
}
op_buffer = boxContext.getOutputChunk(0);
m_encoder->process(OVP_GD_Algorithm_SignalEncoder_InputTriggerId_EncodeBuffer);
boxContext.markOutputAsReadyToSend(0, m_lastChunkEndTime, m_lastChunkEndTime + m_chunkDuration);
m_lastChunkEndTime += m_chunkDuration;
#if defined _DEBUG
const double diff = CTime(m_lastChunkEndTime).toSeconds() - CTime(time).toSeconds();
if (std::abs(diff) > 1) { this->getLogManager() << Kernel::LogLevel_Warning << "Time difference detected: " << diff << " secs.\n"; }
#endif
}
return true;
}
void CBoxAlgorithmVRPNAnalogClient::setAnalog(const size_t nAnalog, const double* analog)
{
// Count how many samples are encoded in pAnalog
const size_t nSample = nAnalog / m_nChannel;
for (size_t i = 0; i < nSample; ++i)
{
// Append the sample to the buffer
for (size_t j = 0; j < m_nChannel; ++j) { m_lastSamples[j] = analog[j * nSample + i]; }
m_sampleBuffer.push_back(m_lastSamples);
// Drop the oldest sample if our buffer got full. This means that VRPN is sending data faster than we consume.
if (m_sampleBuffer.size() > m_nSamplePerSentBlock) { m_sampleBuffer.pop_front(); }
}
}
#endif // TARGET_HAS_ThirdPartyVRPN
} // namespace VRPN
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,96 @@
#pragma once
#if defined TARGET_HAS_ThirdPartyVRPN
#include "../ovp_defines.h"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <vector>
#include <deque>
#include <vrpn_Analog.h>
namespace OpenViBE {
namespace Plugins {
namespace VRPN {
class CBoxAlgorithmVRPNAnalogClient final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
void release() override { delete this; }
uint64_t getClockFrequency() override { return 128LL << 32; } // the box clock frequency
bool initialize() override;
bool uninitialize() override;
bool processClock(Kernel::CMessageClock& msg) override;
bool process() override;
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_VRPNAnalogClient)
void setAnalog(size_t nAnalog, const double* analog);
protected:
uint64_t m_lastChunkEndTime = 0;
uint64_t m_chunkDuration = 0;
size_t m_sampling = 0;
size_t m_nChannel = 0;
size_t m_nSamplePerSentBlock = 0;
bool m_firstStart = false;
Kernel::IAlgorithmProxy* m_encoder = nullptr;
Kernel::TParameterHandler<CMatrix*> ip_matrix;
Kernel::TParameterHandler<uint64_t> ip_sampling;
Kernel::TParameterHandler<IMemoryBuffer*> op_buffer;
CString m_peripheralName;
std::deque<std::vector<double>> m_sampleBuffer; // Used as a limited-size buffer of sample vectors
std::vector<double> m_lastSamples; // The last sample received from VRPN
vrpn_Analog_Remote* m_vrpnAnalogRemote = nullptr;
};
class CBoxAlgorithmVRPNAnalogClientDesc final : public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return CString("Analog VRPN Client"); }
CString getAuthorName() const override { return CString("Yann Renard"); }
CString getAuthorCompanyName() const override { return CString("INRIA"); }
CString getShortDescription() const override
{
return CString("Connects to an external VRPN device and translate an analog information into OpenViBE signal");
}
CString getDetailedDescription() const override { return CString("-"); }
CString getCategory() const override { return CString("Acquisition and network IO/VRPN"); }
CString getVersion() const override { return CString("1.0"); }
CString getStockItemName() const override { return CString("gtk-connect"); }
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_VRPNAnalogClient; }
IPluginObject* create() override { return new CBoxAlgorithmVRPNAnalogClient; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
// prototype.addInput ("input name", /* input type (OV_TypeId_Signal) */);
prototype.addOutput("Output", OV_TypeId_Signal);
prototype.addSetting("Peripheral name", OV_TypeId_String, "openvibe-vrpn@localhost");
prototype.addSetting("Sampling Rate", OV_TypeId_Integer, "512");
prototype.addSetting("Number of Channels", OV_TypeId_Integer, "16");
prototype.addSetting("Sample Count per Sent Block", OV_TypeId_Integer, "32");
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_VRPNAnalogClientDesc)
};
} // namespace VRPN
} // namespace Plugins
} // namespace OpenViBE
#endif // TARGET_HAS_ThirdPartyVRPN
@@ -0,0 +1,137 @@
#if defined TARGET_HAS_ThirdPartyVRPN
#include "ovpCBoxAlgorithmVRPNButtonClient.h"
namespace OpenViBE {
namespace Plugins {
namespace VRPN {
static void VRPN_CALLBACK vrpn_button_cb(void* data, const vrpn_BUTTONCB b)
{
CBoxAlgorithmVRPNButtonClient* box = static_cast<CBoxAlgorithmVRPNButtonClient*>(data);
(box->m_Buttons).push_back(std::pair<size_t, bool>(b.button, b.state ? true : false));
}
bool CBoxAlgorithmVRPNButtonClient::initialize()
{
const size_t nOutput = this->getStaticBoxContext().getOutputCount();
for (size_t i = 0; i < nOutput; ++i)
{
Kernel::IAlgorithmProxy* encoder = &this->getAlgorithmManager().getAlgorithm(
this->getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StimulationEncoder));
encoder->initialize();
CStimulationSet* stimSet = new CStimulationSet();
Kernel::TParameterHandler<IStimulationSet*> ip_StimSet(encoder->getInputParameter(OVP_GD_Algorithm_StimulationEncoder_InputParameterId_StimulationSet));
ip_StimSet = stimSet;
m_encoders.push_back(encoder);
m_stimSets.push_back(stimSet);
m_stimIDsOn.push_back(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), i * 2 + 1));
m_stimIDsOff.push_back(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), i * 2 + 2));
}
const CString name = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
m_vrpnButtonRemote = new vrpn_Button_Remote(name.toASCIIString());
m_vrpnButtonRemote->register_change_handler(static_cast<void*>(this), vrpn_button_cb);
m_lastChunkEndTime = uint64_t(-1);
return true;
}
bool CBoxAlgorithmVRPNButtonClient::uninitialize()
{
const size_t nOutput = this->getStaticBoxContext().getOutputCount();
delete m_vrpnButtonRemote;
m_vrpnButtonRemote = nullptr;
for (size_t i = 0; i < nOutput; ++i)
{
delete m_stimSets[i];
m_encoders[i]->uninitialize();
this->getAlgorithmManager().releaseAlgorithm(*m_encoders[i]);
}
m_stimSets.clear();
m_encoders.clear();
m_stimIDsOn.clear();
m_stimIDsOff.clear();
return true;
}
bool CBoxAlgorithmVRPNButtonClient::processClock(Kernel::CMessageClock& /*msg*/)
{
this->getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
bool CBoxAlgorithmVRPNButtonClient::process()
{
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
const size_t nOutput = this->getStaticBoxContext().getOutputCount();
size_t i;
// Clears pending stimulations
for (i = 0; i < nOutput; ++i) { m_stimSets[i]->clear(); }
// Refreshes VRPN device
m_gotStimulation = false;
m_vrpnButtonRemote->mainloop();
while (!m_Buttons.empty())
{
const std::pair<size_t, bool> state = m_Buttons.front();
m_Buttons.pop_front();
setButton(state.first, state.second);
}
// Encodes streams
for (i = 0; i < nOutput; ++i)
{
Kernel::TParameterHandler<IMemoryBuffer*> op_buffer(
m_encoders[i]->getOutputParameter(OVP_GD_Algorithm_StimulationEncoder_OutputParameterId_EncodedMemoryBuffer));
op_buffer = boxContext.getOutputChunk(i);
if (m_lastChunkEndTime == uint64_t(-1))
{
m_encoders[i]->process(OVP_GD_Algorithm_StimulationEncoder_InputTriggerId_EncodeHeader);
boxContext.markOutputAsReadyToSend(i, 0, 0);
}
else
{
if (m_gotStimulation) { m_encoders[i]->process(OVP_GD_Algorithm_StimulationEncoder_InputTriggerId_EncodeBuffer); }
boxContext.markOutputAsReadyToSend(i, m_lastChunkEndTime, this->getPlayerContext().getCurrentTime());
}
}
// Updates timings
m_lastChunkEndTime = this->getPlayerContext().getCurrentTime();
return true;
}
void CBoxAlgorithmVRPNButtonClient::setButton(const size_t index, const bool pressed)
{
const Kernel::IBox& boxContext = this->getStaticBoxContext();
if (index >= boxContext.getOutputCount())
{
this->getLogManager() << Kernel::LogLevel_Warning << "Ignored button " << index + 1 << " with state " << CString(pressed ? "pressed" : "released") << "...\n";
}
else
{
this->getLogManager() << Kernel::LogLevel_Trace << "Changed button " << index + 1 << " with state " << CString(pressed ? "pressed" : "released") << "...\n";
if (pressed) { m_stimSets[index]->appendStimulation(m_stimIDsOn[index], this->getPlayerContext().getCurrentTime(), 0); }
else { m_stimSets[index]->appendStimulation(m_stimIDsOff[index], this->getPlayerContext().getCurrentTime(), 0); }
m_gotStimulation = true;
}
}
#endif // TARGET_HAS_ThirdPartyVRPN
} // namespace VRPN
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,119 @@
#pragma once
#if defined TARGET_HAS_ThirdPartyVRPN
#include "../ovp_defines.h"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <list>
#include <vrpn_Button.h>
namespace OpenViBE {
namespace Plugins {
namespace VRPN {
class CBoxAlgorithmVRPNButtonClient final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
void release() override { delete this; }
uint64_t getClockFrequency() override { return 128LL << 32; } // the box clock frequency
bool initialize() override;
bool uninitialize() override;
bool processClock(Kernel::CMessageClock& msg) override;
bool process() override;
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_VRPNButtonClient)
std::list<std::pair<size_t, bool>> m_Buttons;
void setButton(const size_t index, const bool pressed);
protected:
uint64_t m_lastChunkEndTime = 0;
bool m_gotStimulation = false;
std::vector<Kernel::IAlgorithmProxy*> m_encoders;
std::vector<IStimulationSet*> m_stimSets;
std::vector<uint64_t> m_stimIDsOn;
std::vector<uint64_t> m_stimIDsOff;
vrpn_Button_Remote* m_vrpnButtonRemote = nullptr;
};
class CBoxAlgorithmVRPNButtonClientListener final : public Toolkit::TBoxListener<IBoxListener>
{
public:
static bool check(Kernel::IBox& box)
{
for (size_t i = 0; i < box.getOutputCount(); ++i)
{
const std::string idx = std::to_string(i + 1);
box.setOutputName(i, ("Output " + idx).c_str());
box.setSettingName(i * 2 + 1, ("Button " + idx + " ON").c_str());
box.setSettingName(i * 2 + 2, ("Button " + idx + " OFF").c_str());
}
return true;
}
bool onOutputAdded(Kernel::IBox& box, const size_t index) override
{
box.setOutputType(index, OV_TypeId_Stimulations);
box.addSetting("", OV_TypeId_Stimulation, "OVTK_GDF_Feedback_Continuous");
box.addSetting("", OV_TypeId_Stimulation, "OVTK_GDF_End_Of_Trial");
return check(box);
}
bool onOutputRemoved(Kernel::IBox& box, const size_t index) override
{
box.removeSetting(index * 2 + 2);
box.removeSetting(index * 2 + 1);
return check(box);
}
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
};
class CBoxAlgorithmVRPNButtonClientDesc final : public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return CString("Button VRPN Client"); }
CString getAuthorName() const override { return CString("Yann Renard"); }
CString getAuthorCompanyName() const override { return CString("INRIA"); }
CString getShortDescription() const override
{
return CString("Connects to an external VRPN device and translate a button information into OpenViBE stimulations");
}
CString getDetailedDescription() const override { return CString("-"); }
CString getCategory() const override { return CString("Acquisition and network IO/VRPN"); }
CString getVersion() const override { return CString("1.0"); }
CString getStockItemName() const override { return CString("gtk-connect"); }
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_VRPNButtonClient; }
IPluginObject* create() override { return new CBoxAlgorithmVRPNButtonClient; }
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmVRPNButtonClientListener; }
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
// prototype.addInput ("input name", /* input type (OV_TypeId_Signal) */);
prototype.addOutput("Output", OV_TypeId_Stimulations);
prototype.addSetting("Peripheral name", OV_TypeId_String, "openvibe-vrpn@localhost");
prototype.addSetting("Button 1 ON", OV_TypeId_Stimulation, "OVTK_GDF_Feedback_Continuous");
prototype.addSetting("Button 1 OFF", OV_TypeId_Stimulation, "OVTK_GDF_End_Of_Trial");
prototype.addFlag(Kernel::BoxFlag_CanAddOutput);
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_VRPNButtonClientDesc)
};
} // namespace VRPN
} // namespace Plugins
} // namespace OpenViBE
#endif // TARGET_HAS_ThirdPartyVRPN
@@ -0,0 +1,144 @@
#if defined TARGET_HAS_ThirdPartyVRPN
#include "ovpCVRPNAnalogServer.h"
#include "../ovpIVRPNServerManager.h"
namespace OpenViBE {
namespace Plugins {
namespace VRPN {
bool CVRPNAnalogServer::initialize()
{
const size_t nInput = this->getStaticBoxContext().getInputCount();
// Gets server name, and creates an analog server for this server
const CString name = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
// Creates the stream decoders
for (size_t i = 0; i < nInput; ++i)
{
m_decoders[i] = &this->getAlgorithmManager().getAlgorithm(this->getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StreamedMatrixDecoder));
m_decoders[i]->initialize();
}
// Creates the peripheral
IVRPNServerManager::getInstance().initialize();
IVRPNServerManager::getInstance().addServer(name, m_serverID);
// We don't know the analog count yet before we get a header, so set to zero.
// This convention will avoid client problems that misleadingly report 'no response from server'.
// The client should check that the channel amount is positive and use that as
// an indication of the box having received at least a proper header.
IVRPNServerManager::getInstance().setAnalogCount(m_serverID, 0);
IVRPNServerManager::getInstance().reportAnalog(m_serverID);
return true;
}
bool CVRPNAnalogServer::uninitialize()
{
const size_t nInput = this->getStaticBoxContext().getInputCount();
// Releases decoders
for (size_t i = 0; i < nInput; ++i)
{
m_decoders[i]->uninitialize();
this->getAlgorithmManager().releaseAlgorithm(*m_decoders[i]);
}
m_decoders.clear();
m_nAnalogs.clear();
// Releases the peripheral
IVRPNServerManager::getInstance().uninitialize();
return true;
}
bool CVRPNAnalogServer::processClock(Kernel::CMessageClock& /*msg*/)
{
// Note: This call doesn't seem to be necessary for VRPN sending the
// data with reportAnarog(). Its utility is likely in keeping the
// connection functional during periods with no data.
IVRPNServerManager::getInstance().process();
return true;
}
bool CVRPNAnalogServer::processInput(const size_t /*index*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
bool CVRPNAnalogServer::process()
{
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
const size_t nInput = this->getStaticBoxContext().getInputCount();
for (size_t i = 0; i < nInput; ++i)
{
for (size_t j = 0; j < boxContext.getInputChunkCount(i); ++j)
{
Kernel::TParameterHandler<const IMemoryBuffer*> ip_buffer(
m_decoders[i]->getInputParameter(OVP_GD_Algorithm_StreamedMatrixDecoder_InputParameterId_MemoryBufferToDecode));
Kernel::TParameterHandler<CMatrix*> op_matrix(m_decoders[i]->getOutputParameter(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputParameterId_Matrix));
ip_buffer = boxContext.getInputChunk(i, j);
m_decoders[i]->process();
CMatrix* matrix = op_matrix;
if (m_decoders[i]->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputTriggerId_ReceivedHeader))
{
m_nAnalogs[i] = matrix->getBufferElementCount();
if (m_nAnalogs.size() == nInput)
{
size_t nAnalog = 0;
for (size_t k = 0; k < nInput; ++k) { nAnalog += m_nAnalogs[k]; }
if (IVRPNServerManager::getInstance().setAnalogCount(m_serverID, nAnalog))
{
this->getLogManager() << Kernel::LogLevel_Trace << "Created VRPN analog server for " << nAnalog << " channel(s)\n";
}
else
{
this->getLogManager() << Kernel::LogLevel_Error << "Failed to create VRPN analog server for " << nAnalog << " channel(s)\n";
return false;
}
m_analogSet = true;
}
boxContext.markInputAsDeprecated(i, j);
}
if (m_analogSet)
{
if (m_decoders[i]->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputTriggerId_ReceivedBuffer))
{
if (m_analogSet)
{
size_t offset = 0;
for (size_t k = 0; k < i; ++k) { offset += m_nAnalogs[k]; }
for (size_t k = 0; k < matrix->getBufferElementCount(); ++k)
{
if (!IVRPNServerManager::getInstance().setAnalogState(m_serverID, offset + k, matrix->getBuffer()[k]))
{
getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Warning <<
"Could not set analog state for index "
<< k << "\n";
}
}
IVRPNServerManager::getInstance().reportAnalog(m_serverID);
}
}
if (m_decoders[i]->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputTriggerId_ReceivedEnd)) { }
boxContext.markInputAsDeprecated(i, j);
}
}
}
return true;
}
} // namespace VRPN
} // namespace Plugins
} // namespace OpenViBE
#endif // OVP_HAS_Vrpn
@@ -0,0 +1,95 @@
#pragma once
#if defined TARGET_HAS_ThirdPartyVRPN
#include "../ovp_defines.h"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <map>
namespace OpenViBE {
namespace Plugins {
namespace VRPN {
class CVRPNAnalogServer final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
CVRPNAnalogServer() {}
void release() override { delete this; }
uint64_t getClockFrequency() override { return 64LL << 32; } // 64hz
bool initialize() override;
bool uninitialize() override;
bool processClock(Kernel::CMessageClock& msg) override;
bool processInput(const size_t index) override;
bool process() override;
_IsDerivedFromClass_Final_(IBoxAlgorithm, OVP_ClassId_VRPNAnalogServer)
protected:
CIdentifier m_serverID = CIdentifier::undefined();
bool m_analogSet = false;
std::map<size_t, Kernel::IAlgorithmProxy*> m_decoders;
std::map<size_t, size_t> m_nAnalogs;
};
class CVRPNAnalogServerListener final : public Toolkit::TBoxListener<IBoxListener>
{
public:
static bool check(Kernel::IBox& box)
{
for (size_t i = 0; i < box.getInputCount(); ++i)
{
box.setInputName(i, ("Input " + std::to_string(i + 1)).c_str());
box.setInputType(i, OV_TypeId_StreamedMatrix);
}
return true;
}
bool onInputRemoved(Kernel::IBox& box, const size_t /*index*/) override { return check(box); }
bool onInputAdded(Kernel::IBox& box, const size_t /*index*/) override { return check(box); }
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
};
class CVRPNAnalogServerDesc final : public IBoxAlgorithmDesc
{
public:
CString getName() const override { return CString("Analog VRPN Server"); }
CString getAuthorName() const override { return CString("Bruno Renier/Yann Renard"); }
CString getAuthorCompanyName() const override { return CString("INRIA"); }
CString getShortDescription() const override { return CString("Creates VRPN analog servers (one per input)."); }
CString getDetailedDescription() const override
{
return CString("Creates VRPN analog servers to make data from the plugin's inputs available to VRPN client applications.");
}
CString getCategory() const override { return CString("Acquisition and network IO/VRPN"); }
CString getVersion() const override { return CString("1.0"); }
CString getStockItemName() const override { return CString("gtk-connect"); }
CIdentifier getCreatedClass() const override { return OVP_ClassId_VRPNAnalogServer; }
IPluginObject* create() override { return new CVRPNAnalogServer(); }
IBoxListener* createBoxListener() const override { return new CVRPNAnalogServerListener; }
void release() override { }
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("Input 1", OV_TypeId_StreamedMatrix);
prototype.addSetting("Peripheral name", OV_TypeId_String, "openvibe-vrpn");
prototype.addFlag(Kernel::BoxFlag_CanAddInput);
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_VRPNAnalogServerDesc)
};
} // namespace VRPN
} // namespace Plugins
} // namespace OpenViBE
#endif // OVP_HAS_Vrpn
@@ -0,0 +1,124 @@
#if defined TARGET_HAS_ThirdPartyVRPN
#include "ovpCVRPNButtonServer.h"
#include "../ovpIVRPNServerManager.h"
namespace OpenViBE {
namespace Plugins {
namespace VRPN {
bool CVRPNButtonServer::initialize()
{
const Kernel::IBox* box = getBoxAlgorithmContext()->getStaticBoxContext();
//get server name, and creates a button server for this server
const CString name = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
IVRPNServerManager::getInstance().initialize();
IVRPNServerManager::getInstance().addServer(name, m_serverID);
IVRPNServerManager::getInstance().setButtonCount(m_serverID, box->getInputCount());
m_decoders.resize(box->getInputCount());
//get stim id
for (size_t i = 0; i < box->getInputCount(); ++i)
{
CString onStimulationID = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1 + i * 2);
CString offStimulationID = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2 + i * 2);
m_stimulationPairs[i] = std::pair<uint64_t, uint64_t>(
getBoxAlgorithmContext()->getPlayerContext()->getTypeManager().getEnumerationEntryValueFromName(OVTK_TypeId_Stimulation, onStimulationID),
getBoxAlgorithmContext()->getPlayerContext()->getTypeManager().getEnumerationEntryValueFromName(OVTK_TypeId_Stimulation, offStimulationID));
m_decoders[i] = new Toolkit::TStimulationDecoder<CVRPNButtonServer>;
m_decoders[i]->initialize(*this, i);
}
return true;
}
bool CVRPNButtonServer::uninitialize()
{
for (size_t i = 0; i < m_decoders.size(); ++i) { delete m_decoders[i]; }
m_decoders.clear();
IVRPNServerManager::getInstance().uninitialize();
return true;
}
bool CVRPNButtonServer::processClock(Kernel::CMessageClock& /*msg*/)
{
IVRPNServerManager::getInstance().process();
return true;
}
bool CVRPNButtonServer::processInput(const size_t /*index*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
bool CVRPNButtonServer::process()
{
Kernel::IBoxIO* boxIO = getBoxAlgorithmContext()->getDynamicBoxContext();
for (size_t input = 0; input < getBoxAlgorithmContext()->getStaticBoxContext()->getInputCount(); ++input)
{
m_currInput = input;
for (size_t chunk = 0; chunk < boxIO->getInputChunkCount(input); ++chunk)
{
m_decoders[input]->decode(chunk);
if (m_decoders[input]->isBufferReceived())
{
const IStimulationSet* stimSet = m_decoders[input]->getOutputStimulationSet();
for (size_t s = 0; s < stimSet->getStimulationCount(); ++s)
{
setStimulation(s, stimSet->getStimulationIdentifier(s), stimSet->getStimulationDate(s));
}
}
}
}
return true;
}
void CVRPNButtonServer::setStimulation(const size_t /*index*/, const uint64_t id, const uint64_t /*date*/)
{
const std::pair<uint64_t, uint64_t> stimulationPair = m_stimulationPairs[m_currInput];
if (stimulationPair.first == stimulationPair.second)
{
if (stimulationPair.first == id)
{
getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Trace << "Received TOGGLE stimulation for button "
<< m_currInput << " (" << id << ")\n";
IVRPNServerManager::getInstance().setButtonState(m_serverID, m_currInput,
!IVRPNServerManager::getInstance().getButtonState(m_serverID, m_currInput));
IVRPNServerManager::getInstance().reportButton(m_serverID);
}
}
else
{
if (stimulationPair.first == id)
{
getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Trace << "Received ON stimulation for button "
<< m_currInput << " (" << id << ")\n";
IVRPNServerManager::getInstance().setButtonState(m_serverID, m_currInput, true);
IVRPNServerManager::getInstance().reportButton(m_serverID);
}
if (stimulationPair.second == id)
{
getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Trace << "Received OFF stimulation for button "
<< m_currInput << " (" << id << ")\n";
IVRPNServerManager::getInstance().setButtonState(m_serverID, m_currInput, false);
IVRPNServerManager::getInstance().reportButton(m_serverID);
}
}
}
} // namespace VRPN
} // namespace Plugins
} // namespace OpenViBE
#endif // OVP_HAS_Vrpn
@@ -0,0 +1,130 @@
#pragma once
#if defined TARGET_HAS_ThirdPartyVRPN
#include "../ovp_defines.h"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <map>
#include <cstdio>
namespace OpenViBE {
namespace Plugins {
namespace VRPN {
class CVRPNButtonServer final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
CVRPNButtonServer() {}
void release() override { delete this; }
uint64_t getClockFrequency() override { return 64LL << 32; } // 64 times per second
bool initialize() override;
bool uninitialize() override;
bool processClock(Kernel::CMessageClock& msg) override;
bool processInput(const size_t index) override;
bool process() override;
_IsDerivedFromClass_Final_(IBoxAlgorithm, OVP_ClassId_VRPNButtonServer)
void setStimulation(const size_t index, const uint64_t id, const uint64_t date);
protected:
std::vector<Toolkit::TStimulationDecoder<CVRPNButtonServer>*> m_decoders;
//Start and end time of the last buffer
uint64_t m_startTime = 0;
uint64_t m_endTime = 0;
size_t m_currInput = 0;
CIdentifier m_serverID = CIdentifier::undefined();
//Pairs of start/stop stimulations id
std::map<size_t, std::pair<uint64_t, uint64_t>> m_stimulationPairs;
};
class CVRPNButtonServerListener final : public Toolkit::TBoxListener<IBoxListener>
{
public:
bool check(Kernel::IBox& box) const
{
for (size_t i = 0; i < box.getInputCount(); ++i)
{
box.setInputName(i, ("Input " + std::to_string(i + 1)).c_str());
box.setInputType(i, OV_TypeId_Stimulations);
}
for (size_t i = 0; i < box.getInputCount(); ++i)
{
box.setSettingName(i * 2 + 1, ("Button " + std::to_string(i + 1) + " ON").c_str());
box.setSettingType(i * 2 + 1, OV_TypeId_Stimulation);
box.setSettingName(i * 2 + 2, ("Button " + std::to_string(i + 1) + " OFF").c_str());
box.setSettingType(i * 2 + 2, OV_TypeId_Stimulation);
}
return true;
}
bool onInputRemoved(Kernel::IBox& box, const size_t index) override
{
box.removeSetting(index * 2 + 1);
box.removeSetting(index * 2 + 1);
return this->check(box);
}
bool onInputAdded(Kernel::IBox& box, const size_t /*index*/) override
{
box.addSetting("", OV_TypeId_Stimulation, "OVTK_GDF_Feedback_Continuous");
box.addSetting("", OV_TypeId_Stimulation, "OVTK_GDF_End_Of_Trial");
return this->check(box);
}
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
};
class CVRPNButtonServerDesc final : public IBoxAlgorithmDesc
{
public:
CString getName() const override { return CString("Button VRPN Server"); }
CString getAuthorName() const override { return CString("Yann Renard"); }
CString getAuthorCompanyName() const override { return CString("INRIA"); }
CString getShortDescription() const override { return CString("Creates VRPN button servers (one per input)."); }
CString getDetailedDescription() const override
{
return CString("Creates VRPN button servers to make data from the plugin's inputs available to VRPN client applications.");
}
CString getCategory() const override { return CString("Acquisition and network IO/VRPN"); }
CString getVersion() const override { return CString("1.0"); }
CString getStockItemName() const override { return CString("gtk-connect"); }
CIdentifier getCreatedClass() const override { return OVP_ClassId_VRPNButtonServer; }
IPluginObject* create() override { return new CVRPNButtonServer(); }
IBoxListener* createBoxListener() const override { return new CVRPNButtonServerListener; }
void release() override { }
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("Input 1", OVTK_TypeId_Stimulations);
prototype.addSetting("Peripheral name", OV_TypeId_String, "openvibe-vrpn");
prototype.addSetting("Button 1 ON", OV_TypeId_Stimulation, "OVTK_GDF_Feedback_Continuous");
prototype.addSetting("Button 1 OFF", OV_TypeId_Stimulation, "OVTK_GDF_End_Of_Trial");
prototype.addFlag(Kernel::BoxFlag_CanAddInput);
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_VRPNButtonServerDesc)
};
} // namespace VRPN
} // namespace Plugins
} // namespace OpenViBE
#endif // OVP_HAS_Vrpn
@@ -0,0 +1,251 @@
#if defined TARGET_HAS_ThirdPartyVRPN
#include "ovpIVRPNServerManager.h"
#include <vrpn_Connection.h>
#include <vrpn_Analog.h>
#include <vrpn_Button.h>
#include <vector>
#include <map>
#include <iostream>
namespace OpenViBE {
namespace Plugins {
namespace VRPN {
namespace {
class CVRPNServerManager final : public IVRPNServerManager
{
public:
CVRPNServerManager() { }
~CVRPNServerManager() override { }
bool initialize() override;
bool uninitialize() override;
bool process() override;
bool reportAnalog(const CIdentifier& serverID) override;
bool reportButton(const CIdentifier& serverID) override;
bool addServer(const CString& name, CIdentifier& identifier) override;
bool isServer(const CIdentifier& identifier) const override;
bool isServer(const CString& name) const override;
bool getServerIdentifier(const CString& name, CIdentifier& identifier) const override;
bool getServerName(const CIdentifier& serverID, CString& name) const override;
bool removeServer(const CIdentifier& serverID) override;
bool setButtonCount(const CIdentifier& serverID, size_t nButton) override;
bool setButtonState(const CIdentifier& serverID, size_t buttonIndex, bool status) override;
bool getButtonState(const CIdentifier& serverID, size_t buttonIndex) const override;
bool setAnalogCount(const CIdentifier& serverID, size_t nAnalog) override;
bool setAnalogState(const CIdentifier& serverID, size_t analogIndex, double status) override;
double getAnalogState(const CIdentifier& serverID, size_t analogIndex) const override;
protected:
vrpn_Connection* m_connection = nullptr;
std::map<CIdentifier, CString> m_serverNames;
std::map<CIdentifier, vrpn_Button_Server*> m_buttonServers;
std::map<CIdentifier, vrpn_Analog_Server*> m_analogServers;
std::map<CIdentifier, std::vector<bool>> m_buttonCaches;
size_t m_nInitialize = 0;
};
} // namespace
IVRPNServerManager& IVRPNServerManager::getInstance()
{
static CVRPNServerManager gVRPNServerManager;
return gVRPNServerManager;
}
bool CVRPNServerManager::initialize()
{
if (!m_nInitialize)
{
//m_Connection=new vrpn_Connection;
m_connection = vrpn_create_server_connection();
}
m_nInitialize++;
return true;
}
bool CVRPNServerManager::uninitialize()
{
m_nInitialize--;
if (!m_nInitialize)
{
for (auto it = m_analogServers.begin(); it != m_analogServers.end(); ++it)
{
if (it->second)
{
delete it->second;
it->second = nullptr;
}
}
m_analogServers.clear();
for (auto it = m_buttonServers.begin(); it != m_buttonServers.end(); ++it)
{
if (it->second)
{
delete it->second;
it->second = nullptr;
}
}
m_buttonServers.clear();
// $$$ UGLY !
// The following function should destroy correctly the connection, but does not.
//vrpn_ConnectionManager::instance().deleteConnection(m_Connection);
delete m_connection;
m_connection = nullptr;
}
return true;
}
bool CVRPNServerManager::process()
{
for (auto it = m_analogServers.begin(); it != m_analogServers.end(); ++it) { if (it->second) { it->second->mainloop(); } }
for (auto it = m_buttonServers.begin(); it != m_buttonServers.end(); ++it) { if (it->second) { it->second->mainloop(); } }
if (m_connection) { m_connection->mainloop(); }
return true;
}
bool CVRPNServerManager::reportAnalog(const CIdentifier& serverID)
{
auto it = m_analogServers.find(serverID);
if (it != m_analogServers.end())
{
if (it->second)
{
// This is public function and mainloop won't call it for me
// Thank you VRPN for this to be similar to button behavior ;o)
it->second->report();
}
}
return true;
}
bool CVRPNServerManager::reportButton(const CIdentifier& serverID)
{
const auto it = m_buttonServers.find(serverID);
if (it != m_buttonServers.end())
{
if (it->second)
{
// This is not public function, however, mainloop calls it for me
// Thank you VRPN for this to be similar to analog behavior ;o)
// itButtonServer->second->report_changes();
}
}
return true;
}
bool CVRPNServerManager::addServer(const CString& name, CIdentifier& identifier)
{
if (this->isServer(name)) { return this->getServerIdentifier(name, identifier); }
identifier = CIdentifier::random();
while (m_serverNames.find(identifier) != m_serverNames.end()) { ++identifier; }
m_serverNames[identifier] = name;
return true;
}
bool CVRPNServerManager::isServer(const CIdentifier& identifier) const { return m_serverNames.find(identifier) != m_serverNames.end(); }
bool CVRPNServerManager::isServer(const CString& name) const
{
for (auto it = m_serverNames.begin(); it != m_serverNames.end(); ++it) { if (it->second == name) { return true; } }
return false;
}
bool CVRPNServerManager::getServerIdentifier(const CString& name, CIdentifier& identifier) const
{
for (auto it = m_serverNames.begin(); it != m_serverNames.end(); ++it)
{
if (it->second == name)
{
identifier = it->first;
return true;
}
}
return false;
}
bool CVRPNServerManager::getServerName(const CIdentifier& serverID, CString& name) const
{
const auto it = m_serverNames.find(serverID);
if (it == m_serverNames.end()) { return false; }
name = it->second;
return true;
}
bool CVRPNServerManager::removeServer(const CIdentifier& serverID)
{
if (!this->isServer(serverID)) { return false; }
// TODO
return true;
}
bool CVRPNServerManager::setButtonCount(const CIdentifier& serverID, const size_t nButton)
{
if (!this->isServer(serverID)) { return false; }
delete m_buttonServers[serverID];
m_buttonServers[serverID] = new vrpn_Button_Server(m_serverNames[serverID], m_connection, int(nButton));
m_buttonCaches[serverID].clear();
m_buttonCaches[serverID].resize(nButton);
return true;
}
bool CVRPNServerManager::setButtonState(const CIdentifier& serverID, const size_t buttonIndex, const bool status)
{
if (!this->isServer(serverID) || m_buttonServers.find(serverID) == m_buttonServers.end()) { return false; }
m_buttonServers[serverID]->set_button(int(buttonIndex), status ? 1 : 0);
m_buttonCaches[serverID][buttonIndex] = status;
return true;
}
bool CVRPNServerManager::getButtonState(const CIdentifier& serverID, const size_t buttonIndex) const
{
if (!this->isServer(serverID)) { return false; }
const auto itButtonServer = m_buttonServers.find(serverID);
if (itButtonServer == m_buttonServers.end()) { return false; }
const auto itButtonCache = m_buttonCaches.find(serverID);
return itButtonCache->second[buttonIndex];
}
bool CVRPNServerManager::setAnalogCount(const CIdentifier& serverID, const size_t nAnalog)
{
if (!this->isServer(serverID)) { return false; }
if (m_analogServers[serverID]) { delete m_analogServers[serverID]; }
m_analogServers[serverID] = new vrpn_Analog_Server(m_serverNames[serverID], m_connection);
if (size_t(m_analogServers[serverID]->setNumChannels(vrpn_uint32(nAnalog))) != nAnalog) { return false; }
return true;
}
bool CVRPNServerManager::setAnalogState(const CIdentifier& serverID, const size_t analogIndex, const double status)
{
if (!this->isServer(serverID) || m_analogServers.find(serverID) == m_analogServers.end()) { return false; }
const size_t nChannel = m_analogServers[serverID]->getNumChannels();
if (analogIndex >= nChannel) { return false; }
m_analogServers[serverID]->channels()[analogIndex] = status;
return true;
}
double CVRPNServerManager::getAnalogState(const CIdentifier& serverID, const size_t analogIndex) const
{
if (!this->isServer(serverID)) { return 0; }
auto it = m_analogServers.find(serverID);
if (it == m_analogServers.end()) { return 0; }
return it->second->channels()[analogIndex];
}
} // namespace VRPN
} // namespace Plugins
} // namespace OpenViBE
#endif // OVP_HAS_Vrpn
@@ -0,0 +1,44 @@
#pragma once
#if defined TARGET_HAS_ThirdPartyVRPN
#include <openvibe/ov_all.h>
namespace OpenViBE {
namespace Plugins {
namespace VRPN {
class IVRPNServerManager
{
public:
static IVRPNServerManager& getInstance();
virtual ~IVRPNServerManager() { }
virtual bool initialize() = 0;
virtual bool uninitialize() = 0;
virtual bool process() = 0;
virtual bool reportAnalog(const CIdentifier& identifier) = 0;
virtual bool reportButton(const CIdentifier& identifier) = 0;
virtual bool addServer(const CString& name, CIdentifier& identifier) = 0;
virtual bool isServer(const CIdentifier& identifier) const = 0;
virtual bool isServer(const CString& name) const = 0;
virtual bool getServerIdentifier(const CString& name, CIdentifier& identifier) const = 0;
virtual bool getServerName(const CIdentifier& identifier, CString& name) const = 0;
virtual bool removeServer(const CIdentifier& identifier) = 0;
virtual bool setButtonCount(const CIdentifier& identifier, size_t nButton) = 0;
virtual bool setButtonState(const CIdentifier& identifier, size_t buttonIndex, bool status) = 0;
virtual bool getButtonState(const CIdentifier& identifier, size_t buttonIndex) const = 0;
virtual bool setAnalogCount(const CIdentifier& serverID, size_t nAnalog) = 0;
virtual bool setAnalogState(const CIdentifier& serverID, size_t analogIndex, double status) = 0;
virtual double getAnalogState(const CIdentifier& serverID, size_t analogIndex) const = 0;
};
} // namespace VRPN
} // namespace Plugins
} // namespace OpenViBE
#endif // OVP_HAS_Vrpn
@@ -0,0 +1,19 @@
#pragma once
// Boxes
//---------------------------------------------------------------------------------------------------
#define OVP_ClassId_VRPNAnalogServer OpenViBE::CIdentifier(0x0DDC3A7E, 0x6F6E6401)
#define OVP_ClassId_VRPNAnalogServerDesc OpenViBE::CIdentifier(0xF54B8E03, 0xAAFF15C6)
#define OVP_ClassId_VRPNButtonServer OpenViBE::CIdentifier(0x0E382E6F, 0x5BE1F00C)
#define OVP_ClassId_VRPNButtonServerDesc OpenViBE::CIdentifier(0xBC86F256, 0x002495EF)
#define OVP_ClassId_BoxAlgorithm_VRPNAnalogClient OpenViBE::CIdentifier(0x7CF4A95E, 0x7270D07B)
#define OVP_ClassId_BoxAlgorithm_VRPNAnalogClientDesc OpenViBE::CIdentifier(0x77B2AE79, 0xFDC31871)
#define OVP_ClassId_BoxAlgorithm_VRPNButtonClient OpenViBE::CIdentifier(0x40714327, 0x458877D2)
#define OVP_ClassId_BoxAlgorithm_VRPNButtonClientDesc OpenViBE::CIdentifier(0x16FB6283, 0x45EC313F)
// Global defines
//---------------------------------------------------------------------------------------------------
#ifdef TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
#include "ovp_global_defines.h"
#endif // TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
@@ -0,0 +1,27 @@
#include <openvibe/ov_all.h>
#include "ovp_defines.h"
#include "box-algorithms/ovpCVRPNAnalogServer.h"
#include "box-algorithms/ovpCVRPNButtonServer.h"
#include "box-algorithms/ovpCBoxAlgorithmVRPNAnalogClient.h"
#include "box-algorithms/ovpCBoxAlgorithmVRPNButtonClient.h"
#include <vector>
namespace OpenViBE {
namespace Plugins {
namespace VRPN {
OVP_Declare_Begin()
#if defined TARGET_HAS_ThirdPartyVRPN
OVP_Declare_New(CVRPNAnalogServerDesc);
OVP_Declare_New(CVRPNButtonServerDesc);
OVP_Declare_New(CBoxAlgorithmVRPNAnalogClientDesc);
OVP_Declare_New(CBoxAlgorithmVRPNButtonClientDesc);
#endif
OVP_Declare_End()
} // namespace VRPN
} // namespace Plugins
} // namespace OpenViBE