Commit f337e77e authored by Jussi Lindgren's avatar Jussi Lindgren

Server: Miscellaneous fixes and changes

- Fixed some potential deadlocks and issues in the client thread concurrency control
- Fixed client connection potentially being released before the client thread exit
- Added a warning print if the server doesn't get a steady clock
- Changed pending sample buffer to std::deque for improved
  efficiency on high sampling rates. Updated the plugin interfaces accordingly.
- Removed the truncation of the drift measure to zero, no longer
  needed as the GUI view is less often updated
- Added several sampling rates (including high freqs such as 44100)
  to Generic Oscillator for debugging.
- The server will now clear the pending buffers on pressing 'Stop'.
- Changed some iterator definitions to 'auto' for clarity
- Fixed gcc warnings about %llu and uint64
- Added some comments
- Code cleanup
parent 7362146b
......@@ -8,6 +8,7 @@
#include "boost/variant.hpp"
#include <map>
#include <deque>
/**
* \brief Interface for acquisition server plugins
......@@ -40,7 +41,7 @@ namespace OpenViBEAcquisitionServer
* This hook is called before sending the stimulations or signal to the connected clients.
* It gets a reference to the current signal buffer and the stimulation set with its start and end dates.
*/
virtual void loopHook(std::vector < std::vector < OpenViBE::float32 > >& vPendingBuffer,
virtual void loopHook(std::deque < std::vector < OpenViBE::float32 > >& vPendingBuffer,
OpenViBE::CStimulationSet& oStimulationSet,
OpenViBE::uint64 start,
OpenViBE::uint64 end,
......
......@@ -54,7 +54,10 @@
</row>
<row>
<col id="0" translatable="yes">512</col>
</row>
</row>
<row>
<col id="0" translatable="yes">1000</col>
</row>
<row>
<col id="0" translatable="yes">1024</col>
</row>
......@@ -64,6 +67,18 @@
<row>
<col id="0" translatable="yes">4096</col>
</row>
<row>
<col id="0" translatable="yes">9600</col>
</row>
<row>
<col id="0" translatable="yes">19200</col>
</row>
<row>
<col id="0" translatable="yes">44100</col>
</row>
<row>
<col id="0" translatable="yes">48000</col>
</row>
</data>
</object>
<object class="GtkDialog" id="openvibe-acquisition-server-settings">
......
......@@ -138,7 +138,7 @@ boolean CConfigurationLabStreamingLayer::preConfigure(void)
else
{
char l_sBuffer[1024];
sprintf(l_sBuffer, "%lu", m_ui32FallbackSamplingRate);
sprintf(l_sBuffer, "%u", static_cast<unsigned int>(m_ui32FallbackSamplingRate));
::gtk_entry_set_text(l_pFallbackSamplingRateEntry, l_sBuffer);
}
......@@ -197,7 +197,12 @@ boolean CConfigurationLabStreamingLayer::postConfigure(void)
// Retrieve fallback sampling rate
::GtkEntry* l_pFallbackSamplingRateEntry = GTK_ENTRY(gtk_builder_get_object(m_pBuilderConfigureInterface, "entry_fallback_sampling_frequency"));
if (::sscanf(gtk_entry_get_text(l_pFallbackSamplingRateEntry), "%lu", &m_ui32FallbackSamplingRate) != 1)
unsigned int l_uiFallbackSamplingRate = 0;
if (::sscanf(gtk_entry_get_text(l_pFallbackSamplingRateEntry), "%u", &l_uiFallbackSamplingRate) == 1)
{
m_ui32FallbackSamplingRate = static_cast<uint32>(l_uiFallbackSamplingRate);
}
else
{
m_ui32FallbackSamplingRate = 0;
}
......
......@@ -28,8 +28,8 @@ namespace OpenViBEAcquisitionServer
OpenViBE::uint64 m_ui64ConnectionTime;
OpenViBE::uint64 m_ui64StimulationTimeOffset;
OpenViBE::uint64 m_ui64SignalSampleCountToSkip;
CConnectionClientHandlerThread* m_pConnectionClientHandlerThread;
std::thread* m_pConnectionClientHandlerStdThread;
CConnectionClientHandlerThread* m_pConnectionClientHandlerThread; // Ptr to the class object that is executed by the client connection handler thread
std::thread* m_pConnectionClientHandlerStdThread; // The actual thread handle
bool m_bChannelUnitsSent;
bool m_bChannelLocalisationSent;
} SConnectionInfo;
......@@ -147,7 +147,7 @@ namespace OpenViBEAcquisitionServer
std::list < std::pair < Socket::IConnection*, SConnectionInfo > > m_vConnection;
std::list < std::pair < Socket::IConnection*, SConnectionInfo > > m_vPendingConnection;
std::vector < std::vector < OpenViBE::float32 > > m_vPendingBuffer;
std::deque < std::vector < OpenViBE::float32 > > m_vPendingBuffer;
std::vector < OpenViBE::float32 > m_vSwapBuffer;
std::vector < OpenViBE::float32 > m_vOverSamplingSwapBuffer;
std::vector < OpenViBE::float64 > m_vImpedance;
......
......@@ -305,9 +305,9 @@ CAcquisitionServerGUI::~CAcquisitionServerGUI(void)
::fprintf(l_pFile, "AcquisitionServer_LastConnectionPort = %i\n", this->getTCPPort());
::fprintf(l_pFile, "# Last Preferences set in the acquisition server\n");
::fprintf(l_pFile, "AcquisitionServer_DriftCorrectionPolicy = %s\n", m_pAcquisitionServer->m_oDriftCorrection.getDriftCorrectionPolicyStr().toASCIIString());
::fprintf(l_pFile, "AcquisitionServer_JitterEstimationCountForDrift = %llu\n", m_pAcquisitionServer->m_oDriftCorrection.getJitterEstimationCountForDrift());
::fprintf(l_pFile, "AcquisitionServer_DriftToleranceDuration = %llu\n", m_pAcquisitionServer->m_oDriftCorrection.getDriftToleranceDurationMs());
::fprintf(l_pFile, "AcquisitionServer_OverSamplingFactor = %llu\n", m_pAcquisitionServer->getOversamplingFactor());
::fprintf(l_pFile, "AcquisitionServer_JitterEstimationCountForDrift = %u\n", static_cast<unsigned int>(m_pAcquisitionServer->m_oDriftCorrection.getJitterEstimationCountForDrift()));
::fprintf(l_pFile, "AcquisitionServer_DriftToleranceDuration = %u\n", static_cast<unsigned int>(m_pAcquisitionServer->m_oDriftCorrection.getDriftToleranceDurationMs()));
::fprintf(l_pFile, "AcquisitionServer_OverSamplingFactor = %u\n", static_cast<unsigned int>(m_pAcquisitionServer->getOversamplingFactor()));
::fprintf(l_pFile, "AcquisitionServer_ChannelSelection = %s\n", (m_pAcquisitionServer->isChannelSelectionRequested() ? "True" : "False"));
::fprintf(l_pFile, "AcquisitionServer_NaNReplacementPolicy = %s\n", m_pAcquisitionServer->getNaNReplacementPolicyStr().toASCIIString());
......@@ -339,8 +339,7 @@ CAcquisitionServerGUI::~CAcquisitionServerGUI(void)
::fclose(l_pFile);
}
vector<IDriver*>::iterator itDriver;
for(itDriver=m_vDriver.begin(); itDriver!=m_vDriver.end(); itDriver++)
for(auto itDriver=m_vDriver.begin(); itDriver!=m_vDriver.end(); itDriver++)
{
m_rKernelContext.getLogManager() << LogLevel_Debug << "Deleting " << (*itDriver)->getName() << "\n";
delete (*itDriver);
......@@ -410,18 +409,19 @@ boolean CAcquisitionServerGUI::initialize(void)
::GtkTreeStore* l_pDriverTreeStore=gtk_tree_store_new(1, G_TYPE_STRING);
gtk_combo_box_set_model(l_pComboBoxDriver, GTK_TREE_MODEL(l_pDriverTreeStore));
vector<IDriver*>::size_type i;
string l_sDefaultDriverName=m_rKernelContext.getConfigurationManager().expand("${AcquisitionServer_DefaultDriver}").toASCIIString();
transform(l_sDefaultDriverName.begin(), l_sDefaultDriverName.end(), l_sDefaultDriverName.begin(), ::to_lower<string::value_type>);
for(i=0; i<m_vDriver.size(); i++)
for(size_t i=0; i<m_vDriver.size(); i++) // n.b. dont use iterator here as we need a numeric index for gtk later anyway
{
IDriver *l_pDriver = m_vDriver[i];
::GtkTreeIter l_oIter;
gtk_tree_store_append(l_pDriverTreeStore, &l_oIter, NULL);
string l_sDriverName=m_vDriver[i]->getName();
string l_sDriverName=l_pDriver->getName();
const bool l_bUnstable = m_vDriver[i]->isFlagSet(DriverFlag_IsUnstable);
const bool l_bDeprecated = m_vDriver[i]->isFlagSet(DriverFlag_IsDeprecated);
const bool l_bUnstable = l_pDriver->isFlagSet(DriverFlag_IsUnstable);
const bool l_bDeprecated = l_pDriver->isFlagSet(DriverFlag_IsDeprecated);
const std::string l_sStringToDisplay =
std::string((l_bUnstable || l_bDeprecated) ? "<span foreground=\"#6f6f6f\">" : "")
......@@ -562,19 +562,13 @@ void CAcquisitionServerGUI::setDriftMs(float64 f64DriftMs)
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(gtk_builder_get_object(m_pBuilderInterface, "progressbar_drift_2")), l_f64DriftRatio);
}
if(std::abs(f64DriftMs)<0.01)
{
// To keep the display steady when fluctuating around 0
f64DriftMs = 0;
}
if(l_bDriftWarning)
{
::sprintf(l_sLabel, "<b>Device drift is too high</b> : %3.2lf ms\n<small>late &lt;-- (tolerance is set to %llu ms) --&gt; early</small>", f64DriftMs, l_ui64DriftToleranceDurationMs);
::sprintf(l_sLabel, "<b>Device drift is too high</b> : %3.2lf ms\n<small>late &lt;-- (tolerance is set to %u ms) --&gt; early</small>", f64DriftMs, static_cast<unsigned int>(l_ui64DriftToleranceDurationMs));
}
else
{
::sprintf(l_sLabel, "Device drift : %3.2lf ms\n<small>late &lt;-- (tolerance is set to %llu ms) --&gt; early</small>", f64DriftMs, l_ui64DriftToleranceDurationMs);
::sprintf(l_sLabel, "Device drift : %3.2lf ms\n<small>late &lt;-- (tolerance is set to %u ms) --&gt; early</small>", f64DriftMs, static_cast<unsigned int>(l_ui64DriftToleranceDurationMs));
}
::gtk_label_set_markup(GTK_LABEL(gtk_builder_get_object(m_pBuilderInterface, "label_drift")), l_sLabel);
}
......@@ -660,16 +654,16 @@ void CAcquisitionServerGUI::buttonConnectToggledCB(::GtkToggleButton* pButton)
{
// Impedance window creation
{
uint64 l_ui64ColumnCount=m_rKernelContext.getConfigurationManager().expandAsInteger("${AcquisitionServer_CheckImpedance_ColumnCount}", 8);
uint32 l_ui32LineCount=(uint32)(m_oHeaderCopy.getChannelCount()/l_ui64ColumnCount);
uint32 l_ui32LastCount=(uint32)(m_oHeaderCopy.getChannelCount()%l_ui64ColumnCount);
const uint64 l_ui64ColumnCount=m_rKernelContext.getConfigurationManager().expandAsInteger("${AcquisitionServer_CheckImpedance_ColumnCount}", 8);
const uint32 l_ui32LineCount=(uint32)(m_oHeaderCopy.getChannelCount()/l_ui64ColumnCount);
const uint32 l_ui32LastCount=(uint32)(m_oHeaderCopy.getChannelCount()%l_ui64ColumnCount);
::GtkWidget* l_pTable=gtk_table_new((gint)(l_ui32LineCount+(l_ui32LastCount?1:0)), (gint)((l_ui32LineCount?l_ui64ColumnCount:l_ui32LastCount)), true);
for(uint32 i=0; i<m_oHeaderCopy.getChannelCount(); i++)
{
uint32 j=(uint32)(i/l_ui64ColumnCount);
uint32 k=(uint32)(i%l_ui64ColumnCount);
const uint32 j=(uint32)(i/l_ui64ColumnCount);
const uint32 k=(uint32)(i%l_ui64ColumnCount);
::GtkWidget* l_pProgressBar=::gtk_progress_bar_new();
::gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(l_pProgressBar), GTK_PROGRESS_BOTTOM_TO_TOP);
::gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(l_pProgressBar), 0);
......@@ -954,15 +948,14 @@ void CAcquisitionServerGUI::scanPluginSettings()
m_vPluginProperties.clear();
for(std::vector<IAcquisitionServerPlugin*>::iterator itp = l_vPlugins.begin(); itp != l_vPlugins.end(); ++itp)
for(auto itp = l_vPlugins.begin(); itp != l_vPlugins.end(); ++itp)
{
IAcquisitionServerPlugin* l_pPlugin = dynamic_cast<IAcquisitionServerPlugin*>(*itp);
SettingsHelper& tmp = l_pPlugin->getSettingsHelper();
const std::map<OpenViBE::CString, Property*>& props = tmp.getAllProperties();
std::map<OpenViBE::CString, Property*>::const_iterator prop_it = props.begin();
for(;prop_it!=props.end();++prop_it) {
for(auto prop_it = props.begin();prop_it!=props.end();++prop_it) {
m_vPluginProperties.push_back( PropertyAndWidget(prop_it->second, NULL) );
}
......@@ -974,7 +967,7 @@ void CAcquisitionServerGUI::savePluginSettings()
{
vector<IAcquisitionServerPlugin*> l_vPlugins = m_pAcquisitionServer->getPlugins();
for(std::vector<IAcquisitionServerPlugin*>::iterator itp = l_vPlugins.begin(); itp != l_vPlugins.end(); ++itp)
for(auto itp = l_vPlugins.begin(); itp != l_vPlugins.end(); ++itp)
{
IAcquisitionServerPlugin* l_pPlugin = dynamic_cast<IAcquisitionServerPlugin*>(*itp);
......
......@@ -55,6 +55,7 @@ namespace OpenViBEAcquisitionServer
l_ui32ClientCount = m_rAcquisitionServer.getClientCount();
l_f64DriftMs = (m_ui32Status == Status_Started ? m_rAcquisitionServer.m_oDriftCorrection.getDriftMs() : 0);
switch (m_ui32Status)
{
case Status_Idle:
......@@ -112,7 +113,7 @@ namespace OpenViBEAcquisitionServer
gdk_threads_add_idle(idle_updateimpedance_cb, (void *)this);
}
if (std::abs(l_f64DriftMs-m_f64LastDriftMs) > 0.01)
if (std::abs(l_f64DriftMs-m_f64LastDriftMs) > 0)
{
m_f64LastDriftMs = l_f64DriftMs;
......@@ -263,7 +264,7 @@ namespace OpenViBEAcquisitionServer
l_sState = "Receiving and sending...";
}
char l_sLabel[1024];
::sprintf(l_sLabel, "%u client%s connected", (unsigned int)m_ui32ClientCount, (m_ui32ClientCount!=1 ? "s" : ""));
::sprintf(l_sLabel, "%u client%s connected", static_cast<unsigned int>(m_ui32ClientCount), (m_ui32ClientCount!=1 ? "s" : ""));
l_sClientText = l_sLabel;
break;
case Status_Idle:
......@@ -331,15 +332,6 @@ namespace OpenViBEAcquisitionServer
};
}
static gboolean idle_updateclientcount_cb(void* pUserData)
{
OpenViBEAcquisitionServer::CAcquisitionServerThread* l_pPtr = static_cast<OpenViBEAcquisitionServer::CAcquisitionServerThread*>(pUserData);
l_pPtr->updateGUIStatus(); // Delegate to GUI status change
return FALSE; // don't call again
}
static gboolean idle_updateimpedance_cb(void* pUserData)
{
OpenViBEAcquisitionServer::CAcquisitionServerThread* l_pPtr = static_cast<OpenViBEAcquisitionServer::CAcquisitionServerThread*>(pUserData);
......
......@@ -270,7 +270,7 @@ boolean CDriftCorrection::estimateDrift(const uint64 ui64NewSamples)
}
boolean CDriftCorrection::correctDrift(int64 i64Correction, uint64& ui64TotalSamples, std::vector < std::vector < OpenViBE::float32 > >& vPendingBuffer, OpenViBE::CStimulationSet& oPendingStimulationSet,
boolean CDriftCorrection::correctDrift(int64 i64Correction, uint64& ui64TotalSamples, std::deque < std::vector < OpenViBE::float32 > >& vPendingBuffer, OpenViBE::CStimulationSet& oPendingStimulationSet,
const std::vector < OpenViBE::float32 >& vPaddingBuffer)
{
if(!m_bStarted)
......
......@@ -5,6 +5,7 @@
#include <vector>
#include <list>
#include <deque>
namespace OpenViBEAcquisitionServer
{
......@@ -46,11 +47,12 @@ namespace OpenViBEAcquisitionServer
// \param oPendingStimulationSet [in/out] : The stimulation set to be realigned
// \param vPaddingBuffer[in] : The sample to repeatedly add if i64Correction > 0
virtual OpenViBE::boolean correctDrift(const OpenViBE::int64 i64Correction, OpenViBE::uint64& ui64TotalSamples,
std::vector < std::vector < OpenViBE::float32 > >& vPendingBuffers, OpenViBE::CStimulationSet& oPendingStimulationSet,
std::deque < std::vector < OpenViBE::float32 > >& vPendingBuffers, OpenViBE::CStimulationSet& oPendingStimulationSet,
const std::vector < OpenViBE::float32 >& vPaddingBuffer );
// Status functions
virtual OpenViBE::boolean isActive(void) const { return m_bIsActive; };
// Prints various statistics but only if drift tolerance was exceeded
virtual void printStats(void) const;
......
......@@ -7,6 +7,8 @@
#include <iostream>
#include <system/ovCTime.h>
#if defined(TARGET_OS_Windows)
#include <Windows.h>
#include <MMSystem.h>
......@@ -31,16 +33,14 @@ boolean parse_arguments(int argc, char** argv, SConfiguration& rConfiguration)
{
SConfiguration l_oConfiguration;
int i;
std::vector < std::string > l_vArgValue;
std::vector < std::string >::const_iterator it;
for(i=1; i<argc; i++)
for(int i=1; i<argc; i++)
{
l_vArgValue.push_back(argv[i]);
}
l_vArgValue.push_back("");
for(it=l_vArgValue.begin(); it!=l_vArgValue.end(); it++)
for(auto it=l_vArgValue.begin(); it!=l_vArgValue.end(); it++)
{
if(*it=="")
{
......@@ -176,8 +176,6 @@ int main(int argc, char ** argv)
}
else
{
// @FIXME CERT what is the correct initialization convention? The legacy style from toolkit crashes.
// OpenViBEToolkit::initialize(*l_pKernelContext);
l_pKernelContext->initialize();
IConfigurationManager& l_rConfigurationManager=l_pKernelContext->getConfigurationManager();
......@@ -190,8 +188,7 @@ int main(int argc, char ** argv)
l_pKernelContext->getPluginManager().addPluginsFromFiles(l_rConfigurationManager.expand("${AcquisitionServer_Plugins}"));
std::map<std::string, std::string>::const_iterator itr;
for(itr=l_oConfiguration.m_oTokenMap.begin();
for(auto itr=l_oConfiguration.m_oTokenMap.begin();
itr!=l_oConfiguration.m_oTokenMap.end();
itr++)
{
......@@ -199,6 +196,12 @@ int main(int argc, char ** argv)
l_rConfigurationManager.addOrReplaceConfigurationToken((*itr).first.c_str(), (*itr).second.c_str());
}
// Check the clock
if(!System::Time::isClockSteady())
{
l_pKernelContext->getLogManager() << LogLevel_Warning << "The system does not seem to have a steady clock. This may affect the acquisition time precision.\n";
}
if(!gtk_init_check(&argc, &argv))
{
l_pKernelContext->getLogManager() << LogLevel_Error << "Unable to initialize GTK. Possibly the display could not be opened. Exiting.\n";
......@@ -227,19 +230,10 @@ int main(int argc, char ** argv)
signal(SIGQUIT, SIG_DFL);
#endif
#if 0 // This is not needed in the acquisition server
if(l_rConfigurationManager.expandAsBoolean("${Kernel_3DVisualisationEnabled}"))
{
l_pKernelContext->getVisualisationManager().initialize3DContext();
}
#endif
{
// If this is encapsulated by gdk_threads_enter() and gdk_threads_exit(), m_pThread->join() can hang when gtk_main() returns before destructor of app has been called.
OpenViBEAcquisitionServer::CAcquisitionServerGUI app(*l_pKernelContext);
try
{
gtk_main();
......@@ -248,8 +242,6 @@ int main(int argc, char ** argv)
{
l_pKernelContext->getLogManager() << LogLevel_Fatal << "Catched top level exception\n";
}
}
cout<<"[ INF ] Application terminated, releasing allocated objects"<<endl;
......
......@@ -64,7 +64,7 @@ void CPluginExternalStimulations::startHook(const std::vector<OpenViBE::CString>
}
void CPluginExternalStimulations::loopHook(std::vector < std::vector < OpenViBE::float32 > >& /* vPendingBuffer */, CStimulationSet &stimulationSet, uint64 start, uint64 end, uint64 /* sampleTime */)
void CPluginExternalStimulations::loopHook(std::deque < std::vector < OpenViBE::float32 > >& /* vPendingBuffer */, CStimulationSet &stimulationSet, uint64 start, uint64 end, uint64 /* sampleTime */)
{
if (m_bIsExternalStimulationsEnabled)
{
......
......@@ -34,7 +34,7 @@ namespace OpenViBEAcquisitionServer
virtual void startHook(const std::vector<OpenViBE::CString>&, OpenViBE::uint32 ui32SamplingFrequency, OpenViBE::uint32 ui32ChannelCount, OpenViBE::uint32 ui32SampleCountPerSentBlock);
virtual void stopHook();
virtual void loopHook(std::vector < std::vector < OpenViBE::float32 > >& vPendingBuffer,
virtual void loopHook(std::deque < std::vector < OpenViBE::float32 > >& vPendingBuffer,
OpenViBE::CStimulationSet& stimulationSet, OpenViBE::uint64 start, OpenViBE::uint64 end, OpenViBE::uint64 sampleTime);
virtual void acceptNewConnectionHook();
......
......@@ -58,7 +58,7 @@ void CPluginTCPTagging::stopHook()
m_scopedTagStream.reset();
}
void CPluginTCPTagging::loopHook(std::vector < std::vector < OpenViBE::float32 > >& /*vPendingBuffer*/,
void CPluginTCPTagging::loopHook(std::deque < std::vector < OpenViBE::float32 > >& /*vPendingBuffer*/,
OpenViBE::CStimulationSet& stimulationSet, uint64 start, uint64 end, uint64 sampleTime)
{
// Get POSIX time (number of milliseconds since epoch)
......
......@@ -41,7 +41,7 @@ namespace OpenViBEAcquisitionServer
void stopHook();
// Overrides virtual method loopHook inherited from class IAcquisitionServerPlugin.
void loopHook(std::vector < std::vector < OpenViBE::float32 > >& vPendingBuffer,
void loopHook(std::deque < std::vector < OpenViBE::float32 > >& vPendingBuffer,
OpenViBE::CStimulationSet& stimulationSet, OpenViBE::uint64 start, OpenViBE::uint64 end, OpenViBE::uint64 sampleTime);
private:
......
......@@ -114,7 +114,7 @@ void CPluginLSLOutput::startHook(const std::vector<OpenViBE::CString>& vSelected
}
void CPluginLSLOutput::loopHook(std::vector < std::vector < OpenViBE::float32 > >& vPendingBuffer, CStimulationSet &stimulationSet, uint64_t start, uint64_t end, uint64_t /* sampleTime */)
void CPluginLSLOutput::loopHook(std::deque < std::vector < OpenViBE::float32 > >& vPendingBuffer, CStimulationSet &stimulationSet, uint64_t start, uint64_t end, uint64_t /* sampleTime */)
{
if (m_bIsLSLOutputEnabled)
{
......
......@@ -29,7 +29,7 @@ namespace OpenViBEAcquisitionServer
virtual void startHook(const std::vector<OpenViBE::CString>& vSelectedChannelNames, uint32_t ui32SamplingFrequency, uint32_t ui32ChannelCount, uint32_t ui32SampleCountPerSentBlock);
virtual void stopHook();
virtual void loopHook(std::vector < std::vector < OpenViBE::float32 > >& vPendingBuffer,
virtual void loopHook(std::deque < std::vector < OpenViBE::float32 > >& vPendingBuffer,
OpenViBE::CStimulationSet& stimulationSet, uint64_t start, uint64_t end, uint64_t sampleTime);
// Plugin implementation
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment