#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 #include #include 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& 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>& 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(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