163 lines
5.1 KiB
C++
Executable File
163 lines
5.1 KiB
C++
Executable File
#if defined TARGET_HAS_ThirdPartyLSL
|
|
|
|
/*
|
|
* Notes: This code should be kept compatible with changes to LSL Input Driver in OpenViBE Acquisition Server,
|
|
* and LSL Export box in Designer.
|
|
*
|
|
*/
|
|
|
|
#include "ovasCPluginLSLOutput.h"
|
|
|
|
#include <vector>
|
|
|
|
#include <system/ovCTime.h>
|
|
#include <labstreamlayer/Utils.hpp>
|
|
|
|
namespace OpenViBE {
|
|
namespace AcquisitionServer {
|
|
namespace Plugins {
|
|
|
|
CPluginLSLOutput::CPluginLSLOutput(const Kernel::IKernelContext& ctx)
|
|
: IAcquisitionServerPlugin(ctx, CString("AcquisitionServer_Plugin_LabStreamingLayerOutput"))
|
|
{
|
|
m_kernelCtx.getLogManager() << Kernel::LogLevel_Info << "Loading plugin: LSL Output\n";
|
|
|
|
m_settings.add("LSL_EnableLSLOutput", &m_IsLSLOutputEnabled);
|
|
m_settings.add("LSL_SignalStreamName", &m_SignalStreamName);
|
|
m_settings.add("LSL_MarkerStreamName", &m_MarkerStreamName);
|
|
m_settings.load();
|
|
|
|
// These are not saved or loaded from .conf as they are supposed to be unique
|
|
m_SignalStreamID = CIdentifier::random().str();
|
|
m_MarkerStreamID = CIdentifier::random().str();
|
|
|
|
while (m_MarkerStreamID == m_SignalStreamID) { m_MarkerStreamID = CIdentifier::random().str(); } // very unlikely
|
|
}
|
|
|
|
CPluginLSLOutput::~CPluginLSLOutput()
|
|
{
|
|
if (m_signalOutlet)
|
|
{
|
|
delete m_signalOutlet;
|
|
m_signalOutlet = nullptr;
|
|
}
|
|
if (m_stimulusOutlet)
|
|
{
|
|
delete m_stimulusOutlet;
|
|
m_stimulusOutlet = nullptr;
|
|
}
|
|
}
|
|
|
|
// Hooks
|
|
|
|
|
|
bool CPluginLSLOutput::startHook(const std::vector<CString>& selectedChannelNames, const size_t sampling, const size_t nChannel,
|
|
const size_t nSamplePerSentBlock)
|
|
{
|
|
m_nSamplePerSentBlock = nSamplePerSentBlock;
|
|
|
|
m_useOVTimestamps = m_kernelCtx.getConfigurationManager().expandAsBoolean("${LSL_UseOVTimestamps}", m_useOVTimestamps);
|
|
m_startTime = System::Time::zgetTime();
|
|
|
|
if (m_IsLSLOutputEnabled)
|
|
{
|
|
m_kernelCtx.getLogManager() << Kernel::LogLevel_Trace << "Will create streams [" << m_SignalStreamName << ", id " << m_SignalStreamID << "] and ["
|
|
<< m_MarkerStreamName << ", id " << m_MarkerStreamID << "]\n";
|
|
|
|
// Open a signal stream
|
|
lsl::stream_info signalInfo(m_SignalStreamName, "signal", int(nChannel), double(sampling), lsl::cf_float32, m_SignalStreamID);
|
|
|
|
lsl::xml_element channels = signalInfo.desc().append_child("channels");
|
|
|
|
for (size_t i = 0; i < nChannel; ++i)
|
|
{
|
|
channels.append_child("channel").append_child_value("label", selectedChannelNames[i].toASCIIString()).append_child_value("unit", "unknown").
|
|
append_child_value("type", "signal");
|
|
}
|
|
|
|
// make a new outlet
|
|
m_signalOutlet = new lsl::stream_outlet(signalInfo, int(m_nSamplePerSentBlock));
|
|
|
|
// Open a stimulus stream
|
|
lsl::stream_info stimulusInfo(m_MarkerStreamName, "Markers", 1, lsl::IRREGULAR_RATE, lsl::cf_int32, m_MarkerStreamID);
|
|
|
|
stimulusInfo.desc().append_child("channels").append_child("channel").append_child_value("label", "Stimulations").append_child_value("type", "marker");
|
|
|
|
m_stimulusOutlet = new lsl::stream_outlet(stimulusInfo);
|
|
|
|
m_kernelCtx.getLogManager() << Kernel::LogLevel_Info << "LSL Output activated...\n";
|
|
}
|
|
|
|
// m_kernelCtx.getLogManager() << Kernel::LogLevel_Info << "Step from sampling rate is " << 1.0 / double(sampling) << "\n";
|
|
|
|
return true;
|
|
}
|
|
|
|
void CPluginLSLOutput::loopHook(std::deque<std::vector<float>>& buffers, CStimulationSet& stimSet, const uint64_t start, const uint64_t end,
|
|
const uint64_t /*sampleTime*/)
|
|
{
|
|
if (m_IsLSLOutputEnabled)
|
|
{
|
|
// Output signal
|
|
if (m_signalOutlet->have_consumers())
|
|
{
|
|
const uint64_t sampleStep = (end - start) / static_cast<uint64_t>(m_nSamplePerSentBlock);
|
|
|
|
if (m_useOVTimestamps)
|
|
{
|
|
const double sampleStepInSec = CTime(sampleStep).toSeconds();
|
|
const double chunkStartInSec = CTime(start).toSeconds();
|
|
for (size_t i = 0; i < m_nSamplePerSentBlock; ++i) { m_signalOutlet->push_sample(buffers[i], chunkStartInSec + double(i) * sampleStepInSec); }
|
|
}
|
|
else
|
|
{
|
|
for (size_t i = 0; i < m_nSamplePerSentBlock; ++i)
|
|
{
|
|
const double lslTime = OpenViBE::LabStreamLayer::getLSLRelativeTime(m_startTime + CTime(start + i * sampleStep));
|
|
m_signalOutlet->push_sample(buffers[i], lslTime);
|
|
}
|
|
}
|
|
|
|
// m_kernelCtx.getLogManager() << Kernel::LogLevel_Info << "Pushed first signal at " << start << "\n";
|
|
// m_kernelCtx.getLogManager() << Kernel::LogLevel_Info << "Step is " << step << "\n";
|
|
}
|
|
|
|
// Output stimuli
|
|
if (m_stimulusOutlet->have_consumers())
|
|
{
|
|
for (size_t i = 0; i < stimSet.getStimulationCount(); ++i)
|
|
{
|
|
if (stimSet.getStimulationDate(i) >= start && stimSet.getStimulationDate(i) < end)
|
|
{
|
|
const int code = int(stimSet.getStimulationIdentifier(i));
|
|
const double date = m_useOVTimestamps ? CTime(stimSet.getStimulationDate(i)).toSeconds()
|
|
: OpenViBE::LabStreamLayer::getLSLRelativeTime(m_startTime + CTime(stimSet.getStimulationDate(i)));
|
|
m_stimulusOutlet->push_sample(&code, date);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPluginLSLOutput::stopHook()
|
|
{
|
|
if (m_IsLSLOutputEnabled)
|
|
{
|
|
if (m_signalOutlet)
|
|
{
|
|
delete m_signalOutlet;
|
|
m_signalOutlet = nullptr;
|
|
}
|
|
if (m_stimulusOutlet)
|
|
{
|
|
delete m_stimulusOutlet;
|
|
m_stimulusOutlet = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace Plugins
|
|
} // namespace AcquisitionServer
|
|
} // namespace OpenViBE
|
|
#endif
|