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,232 @@
#ifdef TARGET_HAS_ThirdPartyLSL
#include "CBoxAlgorithmHelloBidirectionnalGame.hpp"
#include "utils.hpp"
#include <ctime>
#include <iostream>
namespace OpenViBE {
namespace Plugins {
namespace Games {
//--------------------------------------------------------------------------------
bool CBoxAlgorithmHelloBidirectionnalGame::initialize()
{
//---------- Initialize Input/Output ----------
m_signalDecoder.initialize(*this, 0);
m_stimDecoder.initialize(*this, 1);
m_iMatrix = m_signalDecoder.getOutputMatrix();
m_iStimSet = m_stimDecoder.getOutputStimulationSet();
m_signalEncoder.initialize(*this, 0);
m_stimEncoder.initialize(*this, 1);
m_oMatrix = m_signalEncoder.getInputMatrix();
m_oStimSet = m_stimEncoder.getInputStimulationSet();
//---------- Initialize LSL Stream (to Unity) ----------
// These are supposed to be unique, so we don't have them in the box config
m_outSignalID = CIdentifier::random().str();
m_outMarkerID = CIdentifier::random().str();
while (m_outMarkerID == m_outSignalID) { m_outMarkerID = CIdentifier::random().str(); } // very unlikely
this->getLogManager() << Kernel::LogLevel_Trace << "We Will create signal stream [" << m_outSignalName << ", id " << m_outSignalID
<< "] and stimulation stream [" << m_outMarkerName << ", id " << m_outMarkerID << "]\n";
//---------- Create Headers for Output ----------
// Matrix Header
m_oMatrix->resize(1, 1);
m_signalEncoder.encodeHeader();
// Stimulation Header
m_oStimSet->setStimulationCount(0); // reset stimulation output
m_stimEncoder.encodeHeader();
Kernel::IBoxIO& boxCtx = this->getDynamicBoxContext();
boxCtx.markOutputAsReadyToSend(0, m_lastMatrixTime, m_lastMatrixTime); // Makes the output available
boxCtx.markOutputAsReadyToSend(1, m_lastStimTime, m_lastStimTime); // Makes the output available
return true;
}
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
bool CBoxAlgorithmHelloBidirectionnalGame::uninitialize()
{
//---------- Uninitialize Input/Output ----------
m_signalDecoder.uninitialize();
m_stimDecoder.uninitialize();
m_signalEncoder.uninitialize();
m_stimEncoder.uninitialize();
//---------- Delete LSL Stream Inlet/Outlet ----------
if (m_signalOutlet)
{
delete m_signalOutlet;
m_signalOutlet = nullptr;
}
if (m_stimOutlet)
{
delete m_stimOutlet;
m_stimOutlet = nullptr;
}
if (m_signalInlet)
{
m_signalInlet->close_stream();
delete m_signalInlet;
m_signalInlet = nullptr;
}
if (m_stimInlet)
{
m_stimInlet->close_stream();
delete m_stimInlet;
m_stimInlet = nullptr;
}
return true;
}
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
bool CBoxAlgorithmHelloBidirectionnalGame::processInput(const size_t /*index*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
bool CBoxAlgorithmHelloBidirectionnalGame::process()
{
// the dynamic box context describes the current state of the box inputs and outputs (i.e. the chunks)
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
const uint64_t currentTime = getPlayerContext().getCurrentTime();
//---------- Emission Part ----------
// Process signals
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
{
m_signalDecoder.decode(i);
if (m_signalDecoder.isHeaderReceived() && !m_signalOutlet)
{
const size_t samplesPerBlock = m_signalDecoder.getOutputMatrix()->getDimensionSize(1);
const size_t frequency = m_signalDecoder.getOutputSamplingRate();
lsl::stream_info signalInfo = createSignalStreamInfo(m_outSignalName, m_outSignalID, m_signalDecoder.getOutputMatrix(), frequency);
// make a new outlet
try { m_signalOutlet = new lsl::stream_outlet(signalInfo, int(samplesPerBlock)); }
catch (...)
{
this->getLogManager() << "Unable to create signal outlet\n";
return false;
}
}
if (m_signalDecoder.isBufferReceived())
{
sendSignal(m_signalOutlet, m_iMatrix, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
}
//if (m_signalDecoder.isEndReceived()) { }
}
// Process stimuli -> LSL markers.
// Note that stimuli with identifiers not fitting to int will be mangled by a static cast.
for (size_t i = 0; i < boxContext.getInputChunkCount(1); ++i)
{
m_stimDecoder.decode(i);
if (m_stimDecoder.isHeaderReceived() && !m_stimOutlet)
{
// Open a stimulus stream
lsl::stream_info info = createStimulationStreamInfo(m_outMarkerName, m_outMarkerID);
try { m_stimOutlet = new lsl::stream_outlet(info); }
catch (...)
{
this->getLogManager() << "Unable to create marker outlet\n";
return false;
}
}
if (m_stimDecoder.isBufferReceived()) { sendStimulation(m_stimOutlet, m_iStimSet); }
//if (m_stimDecoder.isEndReceived()) { }
}
//---------- Reception Part ----------
if (!m_signalInlet) // We don't have a signal inlet
{
const lsl::stream_info signalInfo = findStreamInfo(m_inSignalName, "", 0); // timeout of 0 to avoid blocking OpenViBE
if (signalInfo.name() == m_inSignalName)
{
m_signalInlet = new lsl::stream_inlet(signalInfo);
try { m_signalInlet->open_stream(); }
catch (...)
{
this->getLogManager() << Kernel::LogLevel_Error << "Failed to open signal stream with name [" << signalInfo.name() << "]\n";
return false;
}
this->getLogManager() << Kernel::LogLevel_Trace << "We have open stream [" << signalInfo.name() << "]\n";
}
}
if (m_signalInlet) // We have a signal inlet
{
double time;
// Signal
try { time = m_signalInlet->pull_sample(&m_bufferLSL, 1, 0.0); } // 2 element timeout to 0.0 to avoid lag (OpenViBE can't have background task)
catch (...)
{
this->getLogManager() << Kernel::LogLevel_Error << "Failed to get signal sample\n";
return false;
}
if (std::abs(time) > 0.0)
{
m_oMatrix->getBuffer()[0] = double(m_bufferLSL);
m_signalEncoder.encodeBuffer(); // Buffer encoded
boxContext.markOutputAsReadyToSend(0, m_lastMatrixTime, currentTime); // Makes the output available
m_lastMatrixTime = currentTime;
}
}
if (!m_stimInlet) // We haven't a stimulation inlet
{
const lsl::stream_info stimInfo = findStreamInfo(m_inMarkerName, "", 0); // timeout of 0 to avoid blocking OpenViBE
if (stimInfo.name() == m_inMarkerName)
{
m_stimInlet = new lsl::stream_inlet(stimInfo);
try { m_stimInlet->open_stream(); }
catch (...)
{
this->getLogManager() << Kernel::LogLevel_Error << "Failed to open marker stream with name [" << stimInfo.name() << "]\n";
return false;
}
this->getLogManager() << Kernel::LogLevel_Trace << "We have open stream [" << stimInfo.name() << "]\n";
}
}
if (m_stimInlet) // We have a stimulation inlet
{
double time;
try { time = m_stimInlet->pull_sample(&m_bufferLSL, 1, 0.0); } // 1 element timeout to 0.0 to avoid lag (OpenViBE can't have background task)
catch (...)
{
this->getLogManager() << Kernel::LogLevel_Error << "Failed to get stimulation sample\n";
return false;
}
if (std::abs(time) > 0.0)
{
m_oStimSet->setStimulationCount(0); // reset stimulation output
m_oStimSet->appendStimulation(uint64_t(m_bufferLSL), currentTime, 0);
m_stimEncoder.encodeBuffer(); // Buffer encoded
boxContext.markOutputAsReadyToSend(1, m_lastStimTime, currentTime); // Makes the output available
m_lastStimTime = currentTime;
this->getLogManager() << Kernel::LogLevel_Info << "Stim received with time " << time << "s in the unity app\n";
}
}
return true;
}
} // namespace Games
} // namespace Plugins
} // namespace OpenViBE
#endif // TARGET_HAS_ThirdPartyLSL
@@ -0,0 +1,108 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CBoxAlgorithmHelloBidirectionnalGame.hpp
/// \brief Class of the box that communicates with Hello Bidirectionnal Game.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 24/02/2021
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#ifdef TARGET_HAS_ThirdPartyLSL
#include "defines.hpp"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <lsl_cpp.h>
#include <ctime>
#include <iostream>
namespace OpenViBE {
namespace Plugins {
namespace Games {
/// <summary> The class CBoxAlgorithmHelloBidirectionnalGame describes the box that sends value in LSL. </summary>
class CBoxAlgorithmHelloBidirectionnalGame final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
void release() override { delete this; }
bool initialize() override;
bool uninitialize() override;
bool processInput(const size_t index) override;
bool process() override;
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, Game_Box_Hello_Bidirectionnal)
protected:
// Encoder / Decoder
Toolkit::TStimulationDecoder<CBoxAlgorithmHelloBidirectionnalGame> m_stimDecoder;
Toolkit::TSignalDecoder<CBoxAlgorithmHelloBidirectionnalGame> m_signalDecoder;
Toolkit::TStimulationEncoder<CBoxAlgorithmHelloBidirectionnalGame> m_stimEncoder;
Toolkit::TSignalEncoder<CBoxAlgorithmHelloBidirectionnalGame> m_signalEncoder;
// Input/ Output
CMatrix *m_iMatrix = nullptr, *m_oMatrix = nullptr;
IStimulationSet *m_iStimSet = nullptr, *m_oStimSet = nullptr;
uint64_t m_lastMatrixTime = 0, m_lastStimTime = 0;
// LSL Stream
lsl::stream_outlet *m_signalOutlet = nullptr, *m_stimOutlet = nullptr;
lsl::stream_inlet *m_signalInlet = nullptr, *m_stimInlet = nullptr;
const std::string m_outSignalName = "ovOutSignal", m_outMarkerName = "ovOutMarkers";
const std::string m_inSignalName = "ovInSignal", m_inMarkerName = "ovInMarkers";
std::string m_outSignalID, m_outMarkerID;
float m_bufferLSL = 0.0;
};
/// <summary> Descriptor of the box Hello Bidirectionnal Game. </summary>
class CBoxAlgorithmHelloBidirectionnalGameDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return "Hello Bidirectionnal Game"; }
CString getAuthorName() const override { return "Thibaut Monseigne"; }
CString getAuthorCompanyName() const override { return "Inria"; }
CString getShortDescription() const override
{
return "Send input stream via LabStreamingLayer (LSL) to the unity game Hello Bidirectionnal and receive back matrix and stimulation.";
}
CString getDetailedDescription() const override
{
return "The Unity Game is in the OpenViBE Unity Game Set of Repository (https://gitlab.inria.fr/openvibe/unity-games/hello-bidirectionnal).\n";
}
CString getCategory() const override { return "Games"; }
CString getVersion() const override { return "0.1"; }
CString getStockItemName() const override { return "gtk-connect"; }
CIdentifier getCreatedClass() const override { return Game_Box_Hello_Bidirectionnal; }
IPluginObject* create() override { return new CBoxAlgorithmHelloBidirectionnalGame; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("Input signal", OV_TypeId_Signal);
prototype.addInput("Input stimulations", OV_TypeId_Stimulations);
prototype.addOutput("Output matrix", OV_TypeId_StreamedMatrix);
prototype.addOutput("Output stimulations", OV_TypeId_Stimulations);
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, Game_Box_Hello_Bidirectionnal_Desc)
};
} // namespace Games
} // namespace Plugins
} // namespace OpenViBE
#endif // TARGET_HAS_ThirdPartyLSL
@@ -0,0 +1,173 @@
#ifdef TARGET_HAS_ThirdPartyLSL
#include "CBoxAlgorithmHelloSenderGame.hpp"
#include "utils.hpp"
#include <ctime>
#include <iostream>
namespace OpenViBE {
namespace Plugins {
namespace Games {
//--------------------------------------------------------------------------------
bool CBoxAlgorithmHelloSenderGame::initialize()
{
m_signalInlet = nullptr;
m_stimInlet = nullptr;
m_signalEncoder.initialize(*this, 0);
m_stimEncoder.initialize(*this, 1);
m_oMatrix = m_signalEncoder.getInputMatrix();
m_oStimSet = m_stimEncoder.getInputStimulationSet();
// Signal Stream
const lsl::stream_info signalInfo = findStreamInfo(m_signalName);
if (signalInfo.name() != m_signalName)
{
this->getLogManager() << Kernel::LogLevel_Error << "Error getting signal stream info for [" << signalInfo.name() << "]\n";
return false;
}
m_signalInlet = new lsl::stream_inlet(signalInfo);
if (!m_signalInlet)
{
this->getLogManager() << Kernel::LogLevel_Error << "Error getting signal inlet for [" << signalInfo.name() << "]\n";
return false;
}
try { m_signalInlet->open_stream(); }
catch (...)
{
this->getLogManager() << Kernel::LogLevel_Error << "Failed to open signal stream with name [" << signalInfo.name() << "]\n";
return false;
}
// Stimulation Stream
const lsl::stream_info stimInfo = findStreamInfo(m_markerName);
if (stimInfo.name() != m_markerName)
{
this->getLogManager() << Kernel::LogLevel_Error << "Error getting marker stream info for [" << stimInfo.name() << "]\n";
return false;
}
m_stimInlet = new lsl::stream_inlet(stimInfo);
if (!m_stimInlet)
{
this->getLogManager() << Kernel::LogLevel_Error << "Error getting marker inlet for [" << stimInfo.name() << "]\n";
return false;
}
try { m_stimInlet->open_stream(); }
catch (...)
{
this->getLogManager() << Kernel::LogLevel_Error << "Failed to open marker stream with name [" << stimInfo.name() << "]\n";
return false;
}
return true;
}
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
bool CBoxAlgorithmHelloSenderGame::uninitialize()
{
m_signalEncoder.uninitialize();
m_stimEncoder.uninitialize();
if (m_signalInlet)
{
m_signalInlet->close_stream();
delete m_signalInlet;
m_signalInlet = nullptr;
}
if (m_stimInlet)
{
m_stimInlet->close_stream();
delete m_stimInlet;
m_stimInlet = nullptr;
}
return true;
}
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
bool CBoxAlgorithmHelloSenderGame::processClock(Kernel::CMessageClock& /*msg*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
bool CBoxAlgorithmHelloSenderGame::process()
{
// the dynamic box context describes the current state of the box inputs and outputs (i.e. the chunks)
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
const uint64_t currentTime = getPlayerContext().getCurrentTime();
// Header
if (!m_headerSent)
{
m_buffer = new float[2];
// Matrix Header
m_oMatrix->resize(2, 1);
m_signalEncoder.encodeHeader();
// Stimulation Header
m_oStimSet->setStimulationCount(0); // reset stimulation output
m_stimEncoder.encodeHeader();
boxContext.markOutputAsReadyToSend(0, m_lastMatrixTime, m_lastMatrixTime); // Makes the output available
boxContext.markOutputAsReadyToSend(1, m_lastStimTime, m_lastStimTime); // Makes the output available
m_headerSent = true;
this->getLogManager() << Kernel::LogLevel_Info << "Header created\n";
}
// Core
double time;
// Signal
try { time = m_signalInlet->pull_sample(m_buffer, 2, 0.0); } // 2 element timeout to 0.0 to avoid lag (OpenViBE can't have background task)
catch (...)
{
this->getLogManager() << Kernel::LogLevel_Error << "Failed to get signal sample\n";
return false;
}
if (std::abs(time) > 0.0)
{
double* buffer = m_oMatrix->getBuffer();
buffer[0] = double(m_buffer[0]);
buffer[1] = double(m_buffer[1]);
m_signalEncoder.encodeBuffer(); // Buffer encoded
boxContext.markOutputAsReadyToSend(0, m_lastMatrixTime, currentTime); // Makes the output available
m_lastMatrixTime = currentTime;
}
// Stimulation
try { time = m_stimInlet->pull_sample(m_buffer, 1, 0.0); } // 1 element timeout to 0.0 to avoid lag (OpenViBE can't have background task)
catch (...)
{
this->getLogManager() << Kernel::LogLevel_Error << "Failed to get stimulation sample\n";
return false;
}
if (std::abs(time) > 0.0)
{
m_oStimSet->setStimulationCount(0); // reset stimulation output
m_oStimSet->appendStimulation(uint64_t(m_buffer[0]), currentTime, 0);
m_stimEncoder.encodeBuffer(); // Buffer encoded
boxContext.markOutputAsReadyToSend(1, m_lastStimTime, currentTime); // Makes the output available
m_lastStimTime = currentTime;
}
return true;
}
//--------------------------------------------------------------------------------
} // namespace Games
} // namespace Plugins
} // namespace OpenViBE
#endif // TARGET_HAS_ThirdPartyLSL
@@ -0,0 +1,99 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CBoxAlgorithmHelloSenderGame.hpp
/// \brief Class of the box that communicate with Hello Sender Game.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 12/03/2020.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#ifdef TARGET_HAS_ThirdPartyLSL
#include "defines.hpp"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <lsl_cpp.h>
#include <ctime>
#include <iostream>
namespace OpenViBE {
namespace Plugins {
namespace Games {
/// <summary> The class CBoxAlgorithmHelloSenderGame describes the box that send value in LSL. </summary>
class CBoxAlgorithmHelloSenderGame final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
void release() override { delete this; }
uint64_t getClockFrequency() override { return 64LL << 32; }
bool initialize() override;
bool uninitialize() override;
bool processClock(Kernel::CMessageClock& msg) override;
bool process() override;
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, Game_Box_Hello_Sender)
protected:
// Decoders
Toolkit::TStimulationEncoder<CBoxAlgorithmHelloSenderGame> m_stimEncoder;
Toolkit::TSignalEncoder<CBoxAlgorithmHelloSenderGame> m_signalEncoder;
CMatrix* m_oMatrix = nullptr;
IStimulationSet* m_oStimSet = nullptr;
float* m_buffer = nullptr;
bool m_headerSent = false;
uint64_t m_lastMatrixTime = 0;
uint64_t m_lastStimTime = 0;
lsl::stream_inlet *m_signalInlet = nullptr, *m_stimInlet = nullptr;
const std::string m_signalName = "ovSignal", m_markerName = "ovMarker";
};
/// <summary> Descriptor of the box Hello Sender Game. </summary>
class CBoxAlgorithmHelloSenderGameDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return "Hello Sender Game"; }
CString getAuthorName() const override { return "Thibaut Monseigne"; }
CString getAuthorCompanyName() const override { return "Inria"; }
CString getShortDescription() const override { return "Receive stream via LabStreamingLayer (LSL) from the unity game Hello Sender."; }
CString getDetailedDescription() const override
{
return "The Unity Game is in the OpenViBE Unity Game Set of Repository (https://gitlab.inria.fr/openvibe/unity-games/hello-sender).\n";
}
CString getCategory() const override { return "Games"; }
CString getVersion() const override { return "0.1"; }
CString getStockItemName() const override { return "gtk-connect"; }
CIdentifier getCreatedClass() const override { return Game_Box_Hello_Sender; }
IPluginObject* create() override { return new CBoxAlgorithmHelloSenderGame; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addOutput("Output matrix", OV_TypeId_StreamedMatrix);
prototype.addOutput("Output stimulations", OV_TypeId_Stimulations);
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, Game_Box_Hello_Sender_Desc)
};
} // namespace Games
} // namespace Plugins
} // namespace OpenViBE
#endif // TARGET_HAS_ThirdPartyLSL
@@ -0,0 +1,124 @@
#ifdef TARGET_HAS_ThirdPartyLSL
#include "CBoxAlgorithmHelloWorldGame.hpp"
#include "utils.hpp"
#include <ctime>
#include <iostream>
namespace OpenViBE {
namespace Plugins {
namespace Games {
//--------------------------------------------------------------------------------
bool CBoxAlgorithmHelloWorldGame::initialize()
{
m_signalOutlet = nullptr;
m_stimOutlet = nullptr;
m_signalDecoder.initialize(*this, 0);
m_stimDecoder.initialize(*this, 1);
m_iMatrix = m_signalDecoder.getOutputMatrix();
m_iStimSet = m_stimDecoder.getOutputStimulationSet();
// These are supposed to be unique, so we don't have them in the box config
m_signalID = CIdentifier::random().str();
m_markerID = CIdentifier::random().str();
while (m_markerID == m_signalID) { m_markerID = CIdentifier::random().str(); } // very unlikely
this->getLogManager() << Kernel::LogLevel_Trace << "We Will create streams [" << m_signalName << ", id " << m_signalID
<< "] and [" << m_markerName << ", id " << m_markerID << "]\n";
return true;
}
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
bool CBoxAlgorithmHelloWorldGame::uninitialize()
{
m_signalDecoder.uninitialize();
m_stimDecoder.uninitialize();
if (m_signalOutlet)
{
delete m_signalOutlet;
m_signalOutlet = nullptr;
}
if (m_stimOutlet)
{
delete m_stimOutlet;
m_stimOutlet = nullptr;
}
return true;
}
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
bool CBoxAlgorithmHelloWorldGame::processInput(const size_t /*index*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
bool CBoxAlgorithmHelloWorldGame::process()
{
// the dynamic box context describes the current state of the box inputs and outputs (i.e. the chunks)
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
// Process signals
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
{
m_signalDecoder.decode(i);
if (m_signalDecoder.isHeaderReceived() && !m_signalOutlet)
{
const size_t samplesPerBlock = m_signalDecoder.getOutputMatrix()->getDimensionSize(1);
const size_t frequency = m_signalDecoder.getOutputSamplingRate();
lsl::stream_info signalInfo = createSignalStreamInfo(m_signalName, m_signalID, m_signalDecoder.getOutputMatrix(), frequency);
// make a new outlet
try { m_signalOutlet = new lsl::stream_outlet(signalInfo, int(samplesPerBlock)); }
catch (...)
{
this->getLogManager() << "Unable to create signal outlet\n";
return false;
}
}
if (m_signalDecoder.isBufferReceived())
{
sendSignal(m_signalOutlet, m_iMatrix, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
}
//if (m_signalDecoder.isEndReceived()) { }
}
// Process stimuli -> LSL markers.
// Note that stimuli with identifiers not fitting to int will be mangled by a static cast.
for (size_t i = 0; i < boxContext.getInputChunkCount(1); ++i)
{
m_stimDecoder.decode(i);
if (m_stimDecoder.isHeaderReceived() && !m_stimOutlet)
{
// Open a stimulus stream
lsl::stream_info info = createStimulationStreamInfo(m_markerName, m_markerID);
try { m_stimOutlet = new lsl::stream_outlet(info); }
catch (...)
{
this->getLogManager() << "Unable to create marker outlet\n";
return false;
}
}
if (m_stimDecoder.isBufferReceived()) { sendStimulation(m_stimOutlet, m_iStimSet); }
//if (m_stimDecoder.isEndReceived()) { }
}
return true;
}
} // namespace Games
} // namespace Plugins
} // namespace OpenViBE
#endif // TARGET_HAS_ThirdPartyLSL
@@ -0,0 +1,94 @@
///-------------------------------------------------------------------------------------------------
///
/// \file CBoxAlgorithmHelloWorldGame.hpp
/// \brief Class of the box that communicate with Hello World Game.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 12/03/2020.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#ifdef TARGET_HAS_ThirdPartyLSL
#include "defines.hpp"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <lsl_cpp.h>
#include <ctime>
#include <iostream>
namespace OpenViBE {
namespace Plugins {
namespace Games {
/// <summary> The class CBoxAlgorithmHelloWorldGame describes the box that send value in LSL. </summary>
class CBoxAlgorithmHelloWorldGame final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
void release() override { delete this; }
bool initialize() override;
bool uninitialize() override;
bool processInput(const size_t index) override;
bool process() override;
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, Game_Box_Hello_World)
protected:
// Decoders
Toolkit::TStimulationDecoder<CBoxAlgorithmHelloWorldGame> m_stimDecoder;
Toolkit::TSignalDecoder<CBoxAlgorithmHelloWorldGame> m_signalDecoder;
CMatrix* m_iMatrix = nullptr;
IStimulationSet* m_iStimSet = nullptr;
lsl::stream_outlet *m_signalOutlet = nullptr, *m_stimOutlet = nullptr;
const std::string m_signalName = "ovSignal", m_markerName = "ovMarkers";
std::string m_signalID, m_markerID;
};
/// <summary> Descriptor of the box Hello World Game. </summary>
class CBoxAlgorithmHelloWorldGameDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return "Hello World Game"; }
CString getAuthorName() const override { return "Thibaut Monseigne"; }
CString getAuthorCompanyName() const override { return "Inria"; }
CString getShortDescription() const override { return "Send input stream via LabStreamingLayer (LSL) to the unity game Hello World."; }
CString getDetailedDescription() const override
{
return "The Unity Game is in the OpenViBE Unity Game Set of Repository (https://gitlab.inria.fr/openvibe/unity-games/hello-world).\n";
}
CString getCategory() const override { return "Games"; }
CString getVersion() const override { return "0.1"; }
CString getStockItemName() const override { return "gtk-connect"; }
CIdentifier getCreatedClass() const override { return Game_Box_Hello_World; }
IPluginObject* create() override { return new CBoxAlgorithmHelloWorldGame; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("Input signal", OV_TypeId_Signal);
prototype.addInput("Input stimulations", OV_TypeId_Stimulations);
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, Game_Box_Hello_World_Desc)
};
} // namespace Games
} // namespace Plugins
} // namespace OpenViBE
#endif // TARGET_HAS_ThirdPartyLSL
@@ -0,0 +1,21 @@
///-------------------------------------------------------------------------------------------------
///
/// \file defines.hpp
/// \brief Defines list for Setting, Shortcut Macro and const.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 12/03/2020.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
// Boxes
//---------------------------------------------------------------------------------------------------
#define Game_Box_Hello_World OpenViBE::CIdentifier(0x46705bc3, 0x3058a939)
#define Game_Box_Hello_World_Desc OpenViBE::CIdentifier(0x3f921b93, 0xadb2b2ec)
#define Game_Box_Hello_Sender OpenViBE::CIdentifier(0x4cbf4237, 0x24f54a7c)
#define Game_Box_Hello_Sender_Desc OpenViBE::CIdentifier(0x5730a8f1, 0x96a608cc)
#define Game_Box_Hello_Bidirectionnal OpenViBE::CIdentifier(0x47616d65, 0x42696469)
#define Game_Box_Hello_Bidirectionnal_Desc OpenViBE::CIdentifier(0x47616d65, 0x42696464)
@@ -0,0 +1,24 @@
#include <openvibe/ov_all.h>
//#include "defines.hpp"
// Boxes Includes
#include "boxes/CBoxAlgorithmHelloWorldGame.hpp"
#include "boxes/CBoxAlgorithmHelloSenderGame.hpp"
#include "boxes/CBoxAlgorithmHelloBidirectionnalGame.hpp"
namespace OpenViBE {
namespace Plugins {
namespace Games {
OVP_Declare_Begin()
#ifdef TARGET_HAS_ThirdPartyLSL
// Register boxes
OVP_Declare_New(CBoxAlgorithmHelloWorldGameDesc)
OVP_Declare_New(CBoxAlgorithmHelloSenderGameDesc)
OVP_Declare_New(CBoxAlgorithmHelloBidirectionnalGameDesc)
#endif // TARGET_HAS_ThirdPartyLSL
OVP_Declare_End()
} // namespace Games
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,91 @@
#include <iostream>
#ifdef TARGET_HAS_ThirdPartyLSL
#include "utils.hpp"
//-------------------------------------------------------------------------------------------------
lsl::stream_info createSignalStreamInfo(const std::string& name, const std::string& id, const OpenViBE::CMatrix* matrix, const size_t frequency)
{
const size_t nChannel = matrix->getDimensionSize(0);
// Open a signal stream
lsl::stream_info res(name, "signal", int(nChannel), double(frequency), lsl::cf_float32, id);
lsl::xml_element channels = res.desc().append_child("channels");
for (size_t c = 0; c < nChannel; ++c)
{
const std::string tmp = matrix->getDimensionLabel(0, c);
channels.append_child("channel").append_child_value("label", tmp).append_child_value("unit", "unknown").append_child_value("type", "signal");
}
return res;
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
lsl::stream_info createStimulationStreamInfo(const std::string& name, const std::string& id)
{
lsl::stream_info res(name, "Markers", 1, lsl::IRREGULAR_RATE, lsl::cf_int32, id);
res.desc().append_child("channels").append_child("channel").append_child_value("label", "Stimulations").append_child_value("type", "marker");
return res;
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void sendSignal(lsl::stream_outlet* outlet, const OpenViBE::CMatrix* matrix, const uint64_t startTime, const uint64_t endTime)
{
if (outlet->have_consumers())
{
const size_t nChannel = matrix->getDimensionSize(0);
const size_t nSamples = matrix->getDimensionSize(1);
const double* iBuffer = matrix->getBuffer();
std::vector<float> buffer(nChannel);
// note: the step computed below should be exactly the same as could be obtained from the sampling rate
const double start = OpenViBE::CTime(startTime).toSeconds();
const double step = OpenViBE::CTime(endTime - startTime).toSeconds() / double(nSamples);
for (size_t s = 0; s < nSamples; ++s)
{
for (size_t c = 0; c < nChannel; ++c) { buffer[c] = float(iBuffer[c * nSamples + s]); }
outlet->push_sample(buffer, start + double(s) * step);
}
}
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void sendStimulation(lsl::stream_outlet* outlet, const OpenViBE::IStimulationSet* stimSet)
{
if (outlet->have_consumers())
{
for (size_t s = 0; s < stimSet->getStimulationCount(); ++s)
{
const int code = int(stimSet->getStimulationIdentifier(s));
const double date = OpenViBE::CTime(stimSet->getStimulationDate(s)).toSeconds();
outlet->push_sample(&code, date);
}
}
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
lsl::stream_info findStreamInfo(const std::string& name, const std::string& id, const int timeout)
{
// Find the signal stream
const std::vector<lsl::stream_info> infos = lsl::resolve_stream("name", name, 1, timeout);
if (infos.empty())
{
if (timeout != 0) { std::cerr << "Failed to find stream with name [" << name << "]\n"; } // Avoid a print if timeout is 0
return lsl::stream_info();
}
for (const auto& i : infos)
{
if (i.source_id() == id) { return i; } // This is the best one
}
return infos[0];
}
//-------------------------------------------------------------------------------------------------
#endif // TARGET_HAS_ThirdPartyLSL
@@ -0,0 +1,62 @@
///-------------------------------------------------------------------------------------------------
///
/// \file utils.hpp
/// \brief Defines some function to facilitate Box Management.
/// \author Thibaut Monseigne (Inria).
/// \version 1.0.
/// \date 12/03/2020.
/// \copyright <a href="https://choosealicense.com/licenses/agpl-3.0/">GNU Affero General Public License v3.0</a>.
///
///-------------------------------------------------------------------------------------------------
#pragma once
#ifdef TARGET_HAS_ThirdPartyLSL
#include <openvibe/ov_all.h>
#include <lsl_cpp.h>
//-------------------------------------------------------------------------------------------------
// Sending
//-------------------------------------------------------------------------------------------------
/// <summary> Create the stream information for signals.</summary>
/// <param name="name"> The name of the stream. </param>
/// <param name="id"> The identifier of the stream. </param>
/// <param name="matrix"> The matrix to get infos (channel number and label).</param>
/// <param name="frequency"> The signal frequency.</param>
/// <returns> the stream info. </returns>
lsl::stream_info createSignalStreamInfo(const std::string& name, const std::string& id, const OpenViBE::CMatrix* matrix, const size_t frequency);
/// <summary> Create the stream information for stimulations.</summary>
/// <param name="name"> The name of the stream. </param>
/// <param name="id"> The identifier of the stream. </param>
/// <returns> the stream info. </returns>
lsl::stream_info createStimulationStreamInfo(const std::string& name, const std::string& id);
/// <summary> Send Signal in LSL outlet. </summary>
/// <param name="outlet"> The LSL outlet. </param>
/// <param name="matrix"> The matrix with the signal to send. </param>
/// <param name="startTime"> The start time of the signal. </param>
/// <param name="endTime"> The end time of the signal. </param>
void sendSignal(lsl::stream_outlet* outlet, const OpenViBE::CMatrix* matrix, const uint64_t startTime, const uint64_t endTime);
/// <summary> Send the stimulation in LSL outlet. </summary>
/// <param name="outlet"> The LSL outlet. </param>
/// <param name="stimSet">The stimulation set to send. </param>
void sendStimulation(lsl::stream_outlet* outlet, const OpenViBE::IStimulationSet* stimSet);
//-------------------------------------------------------------------------------------------------
// Receiving
//-------------------------------------------------------------------------------------------------
/// <summary> Finds the stream information with the function <c>lsl::resolve_stream</c>. </summary>
/// <param name="name"> The expected name of the stream. </param>
/// <param name="id"> Optionally The expected ID of the stream in addition of the name. </param>
/// <param name="timeout"> Optionally a timeout of the operation, in seconds (default: no timeout).
/// If the timeout expires, a default stream info is returned. </param>
/// <returns></returns>
lsl::stream_info findStreamInfo(const std::string& name, const std::string& id = "", const int timeout = LSL_FOREVER);
#endif // TARGET_HAS_ThirdPartyLSL