init
This commit is contained in:
+733
@@ -0,0 +1,733 @@
|
||||
#include <iomanip>
|
||||
#if defined TARGET_HAS_ThirdPartyMatlab
|
||||
|
||||
#include "ovpCBoxAlgorithmMatlabScripting.h"
|
||||
|
||||
#include <system/ovCMath.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
#include <algorithm>
|
||||
|
||||
#include <mex.h>
|
||||
#include <engine.h>
|
||||
|
||||
#include <fs/Files.h>
|
||||
|
||||
#if defined TARGET_OS_Windows
|
||||
#include <windows.h>
|
||||
#include <direct.h>
|
||||
#define getCurrentDir _getcwd
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#define getCurrentDir getcwd
|
||||
#endif
|
||||
|
||||
|
||||
// Size of the internal buffer storing matlab messages, in char
|
||||
#define MATLAB_BUFFER 2048
|
||||
#define MATLAB_ENGINE ((Engine*)m_engineHandle)
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Matlab {
|
||||
|
||||
// Sanitizes a path so that it only has / or \ characters and has a / or \ in the end.
|
||||
// @fixme should move to plugins-FS possibly
|
||||
void CBoxAlgorithmMatlabScripting::sanitizePath(CString& pathToModify)
|
||||
{
|
||||
std::string tmpPath(pathToModify);
|
||||
// Append / to end of path if its not there already
|
||||
if (tmpPath.length() > 0)
|
||||
{
|
||||
const char lastChar = tmpPath.at(tmpPath.length() - 1);
|
||||
if (lastChar != '\\' && lastChar != '/') { tmpPath += "/"; }
|
||||
}
|
||||
|
||||
#if defined TARGET_OS_Windows
|
||||
std::replace(tmpPath.begin(), tmpPath.end(), '/', '\\'); // Convert '/' to '\'
|
||||
#endif
|
||||
pathToModify = tmpPath.c_str();
|
||||
}
|
||||
|
||||
// The checkFailureRoutine() verifies the result of a matlab call (via engine or helper functions) given as argument.
|
||||
// If the result is false (the matlab call failed), the message msg is printed in the Error Log Level, and the macro returns false.
|
||||
// The Matlab output buffer is then printed. If an error message is detected in the buffer, the same error message is printed and
|
||||
// the macro returns false.
|
||||
bool CBoxAlgorithmMatlabScripting::checkFailureRoutine(const bool result, const CString& msg)
|
||||
{
|
||||
OV_ERROR_UNLESS_KRF(result, msg, Kernel::ErrorType::BadProcessing);
|
||||
|
||||
m_errorDetected = false;
|
||||
this->printOutputBufferWithFormat();
|
||||
OV_ERROR_UNLESS_KRF(!m_errorDetected, msg, Kernel::ErrorType::BadProcessing);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatlabScripting::openMatlabEngineSafely()
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Trying to open the Matlab engine\n";
|
||||
#if defined TARGET_OS_Linux
|
||||
m_engineHandle = ::engOpen(m_matlabPath.toASCIIString());
|
||||
OV_ERROR_UNLESS_KRF(m_matlabEngine, "Could not open the Matlab engine.\nThe configured path to the matlab executable was expanded as '" << m_matlabPath << "'.", Kernel::ErrorType::BadProcessing);
|
||||
}
|
||||
#elif defined TARGET_OS_Windows
|
||||
__try { m_engineHandle = engOpen(nullptr); }
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "First call to the MATLAB engine failed.\n";
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "To use this box you must have MATLAB installed on your computer.\n";
|
||||
#if defined(TARGET_ARCHITECTURE_x64)
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "For this build of OpenViBE, MATLAB needs to be a 64bit version.\n";
|
||||
#else
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "For this build of OpenViBE, MATLAB needs to be a 32bit version.\n";
|
||||
#endif
|
||||
m_engineHandle = nullptr;
|
||||
}
|
||||
if (!MATLAB_ENGINE)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Could not open the Matlab engine.\nThe matlab binary path was reasoned to be '" << m_matlabPath << "'.\n";
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
OV_ERROR_KRF("Only Linux and Windows are supported", Kernel::ErrorType::BadVersion);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatlabScripting::initialize()
|
||||
{
|
||||
m_engineHandle = nullptr;
|
||||
m_matlabBuffer = nullptr;
|
||||
m_helper = new CMatlabHelper(this->getLogManager(), this->getErrorManager());
|
||||
|
||||
m_clockFrequency = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
|
||||
for (size_t i = 0; i < getStaticBoxContext().getInputCount(); ++i)
|
||||
{
|
||||
CIdentifier type;
|
||||
getStaticBoxContext().getInputType(i, type);
|
||||
Toolkit::TDecoder<CBoxAlgorithmMatlabScripting>* decoder = nullptr;
|
||||
if (type == OV_TypeId_StreamedMatrix) { decoder = new Toolkit::TStreamedMatrixDecoder<CBoxAlgorithmMatlabScripting>(*this, i); }
|
||||
else if (type == OV_TypeId_ChannelLocalisation) { decoder = new Toolkit::TChannelLocalisationDecoder<CBoxAlgorithmMatlabScripting>(*this, i); }
|
||||
else if (type == OV_TypeId_FeatureVector) { decoder = new Toolkit::TFeatureVectorDecoder<CBoxAlgorithmMatlabScripting>(*this, i); }
|
||||
else if (type == OV_TypeId_Spectrum) { decoder = new Toolkit::TSpectrumDecoder<CBoxAlgorithmMatlabScripting>(*this, i); }
|
||||
else if (type == OV_TypeId_Signal) { decoder = new Toolkit::TSignalDecoder<CBoxAlgorithmMatlabScripting>(*this, i); }
|
||||
else if (type == OV_TypeId_Stimulations) { decoder = new Toolkit::TStimulationDecoder<CBoxAlgorithmMatlabScripting>(*this, i); }
|
||||
else if (type == OV_TypeId_ExperimentInfo) { decoder = new Toolkit::TExperimentInfoDecoder<CBoxAlgorithmMatlabScripting>(*this, i); }
|
||||
else { this->getLogManager() << Kernel::LogLevel_Warning << "Undefined type on input [" << i << "].\n"; }
|
||||
if (decoder != nullptr)
|
||||
{
|
||||
m_decoders.insert(std::make_pair(i, decoder));
|
||||
m_nInputHeaderSent = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < getStaticBoxContext().getOutputCount(); ++i)
|
||||
{
|
||||
CIdentifier type;
|
||||
getStaticBoxContext().getOutputType(i, type);
|
||||
Toolkit::TEncoder<CBoxAlgorithmMatlabScripting>* encoder = nullptr;
|
||||
if (type == OV_TypeId_StreamedMatrix) { encoder = new Toolkit::TStreamedMatrixEncoder<CBoxAlgorithmMatlabScripting>(*this, i); }
|
||||
else if (type == OV_TypeId_ChannelLocalisation) { encoder = new Toolkit::TChannelLocalisationEncoder<CBoxAlgorithmMatlabScripting>(*this, i); }
|
||||
else if (type == OV_TypeId_FeatureVector) { encoder = new Toolkit::TFeatureVectorEncoder<CBoxAlgorithmMatlabScripting>(*this, i); }
|
||||
else if (type == OV_TypeId_Spectrum) { encoder = new Toolkit::TSpectrumEncoder<CBoxAlgorithmMatlabScripting>(*this, i); }
|
||||
else if (type == OV_TypeId_Signal) { encoder = new Toolkit::TSignalEncoder<CBoxAlgorithmMatlabScripting>(*this, i); }
|
||||
else if (type == OV_TypeId_Stimulations) { encoder = new Toolkit::TStimulationEncoder<CBoxAlgorithmMatlabScripting>(*this, i); }
|
||||
else if (type == OV_TypeId_ExperimentInfo) { encoder = new Toolkit::TExperimentInfoEncoder<CBoxAlgorithmMatlabScripting>(*this, i); }
|
||||
else { this->getLogManager() << Kernel::LogLevel_Warning << "Undefined type on input [" << i << "].\n"; }
|
||||
if (encoder != nullptr)
|
||||
{
|
||||
m_encoders.insert(std::make_pair(i, encoder));
|
||||
m_oHeaderStates.insert(std::make_pair(i, false));
|
||||
}
|
||||
}
|
||||
|
||||
m_matlabPath = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
|
||||
|
||||
#if defined TARGET_OS_Windows
|
||||
if (!FS::Files::directoryExists(m_matlabPath) && FS::Files::fileExists(m_matlabPath))
|
||||
{
|
||||
// The path might be pointing to the executable, try to extract the directory
|
||||
char parentPath[MAX_PATH];
|
||||
FS::Files::getParentPath(m_matlabPath, parentPath);
|
||||
m_matlabPath = CString(parentPath);
|
||||
}
|
||||
|
||||
sanitizePath(m_matlabPath);
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Interpreting Matlab path as '" << m_matlabPath << "'\n";
|
||||
|
||||
OV_ERROR_UNLESS_KRF(FS::Files::directoryExists(m_matlabPath), "Configured Matlab path '" << m_matlabPath << "' does not seem to be a directory",
|
||||
Kernel::ErrorType::BadConfig);
|
||||
|
||||
char* path = getenv("PATH");
|
||||
OV_ERROR_UNLESS_KRF(path, "Could not access the environment variable PATH to add Matlab path to it.", Kernel::ErrorType::BadSetting);
|
||||
|
||||
std::string strPath = std::string(path);
|
||||
size_t found = strPath.find(m_matlabPath.toASCIIString());
|
||||
if (found == std::string::npos)
|
||||
{
|
||||
CString pathModif = path + CString(";") + m_matlabPath;
|
||||
OV_ERROR_UNLESS_KRF(_putenv_s("PATH", pathModif) == 0, "Failed to modify the environment PATH with the Matlab path.",
|
||||
Kernel::ErrorType::BadProcessing);
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Matlab Path '" << m_matlabPath << "' added to Windows PATH environment variable.\n";
|
||||
}
|
||||
else { this->getLogManager() << Kernel::LogLevel_Trace << "No need to add matlab to PATH\n"; }
|
||||
#endif
|
||||
|
||||
if (!openMatlabEngineSafely()) { return false; }
|
||||
|
||||
m_matlabBuffer = new char[MATLAB_BUFFER + 1];
|
||||
m_matlabBuffer[MATLAB_BUFFER] = '\0';
|
||||
engOutputBuffer(MATLAB_ENGINE, m_matlabBuffer, MATLAB_BUFFER);
|
||||
m_errorDetected = false;
|
||||
|
||||
// add the openvibe toolbox to matlab path
|
||||
char curDir[FILENAME_MAX];
|
||||
OV_ERROR_UNLESS_KRF(getCurrentDir(curDir, FILENAME_MAX), "Failed to get the execution directory.", Kernel::ErrorType::BadProcessing);
|
||||
|
||||
std::string curDirString = curDir;
|
||||
std::replace(curDirString.begin(), curDirString.end(), '\\', '/');
|
||||
|
||||
this->getConfigurationManager().addOrReplaceConfigurationToken(CString("Path_Bin_Abs"), CString(curDirString.c_str()));
|
||||
|
||||
CString cmd = CString("addpath('") + Directories::getDataDir() + "/plugins/matlab');";
|
||||
engEvalString(MATLAB_ENGINE, cmd.toASCIIString());
|
||||
//if(!checkFailureRoutine(engEvalString(m_matlabEngine, (const char * )l_sOpenvibeToolboxPath) == 0, "An error occurred while adding the path to openvibe toolbox\n")) return false;
|
||||
// If there is more than 1 Matlab box in the scenario, the path is set repeatedly
|
||||
// resulting in warning messages in the buffer. We don't print them.
|
||||
// this->printOutputBufferWithFormat();
|
||||
|
||||
CString workingDir = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2);
|
||||
sanitizePath(workingDir);
|
||||
|
||||
// Try to find an unused box instance variable name
|
||||
while (true)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss.fill('0');
|
||||
ss << "OV_BOX_0x" << std::setw(8) << std::hex << System::Math::randomI() << "_0x" << std::setw(8) << std::hex << System::Math::randomI();
|
||||
m_boxInstanceVariableName = ss.str().c_str();
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Checking if variable " << m_boxInstanceVariableName << " is in use...\n";
|
||||
mxArray* tmp = engGetVariable(MATLAB_ENGINE, m_boxInstanceVariableName.toASCIIString());
|
||||
if (!tmp)
|
||||
{
|
||||
// Found unused name
|
||||
break;
|
||||
}
|
||||
mxDestroyArray(tmp);
|
||||
}
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Selected variable name " << m_boxInstanceVariableName << "\n";
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Setting working directory to " << workingDir << "\n";
|
||||
cmd = CString("cd '") + workingDir + CString("'");
|
||||
if (!checkFailureRoutine(engEvalString(MATLAB_ENGINE, cmd) == 0, "An error occurred while changing the working directory\n")) { return false; }
|
||||
|
||||
// executes the pre-run routine that defines the global identifiers for streams and stimulations codes
|
||||
cmd = CString("run '") + Directories::getDataDir() + "/plugins/matlab/OV_define.m'";
|
||||
if (!checkFailureRoutine(engEvalString(MATLAB_ENGINE, cmd) == 0, "An error occurred while calling OV_define.m")) { return false; }
|
||||
|
||||
cmd = CString("run '") + Directories::getDataDir() + "/plugins/matlab/OV_stimulations.m'";
|
||||
if (!checkFailureRoutine(engEvalString(MATLAB_ENGINE, cmd) == 0, "An error occurred while calling OV_stimulations.m")) { return false; }
|
||||
|
||||
// executes the pre-run routine that construct the ov_box object
|
||||
cmd = m_boxInstanceVariableName + " = OV_createBoxInstance(" + std::to_string(this->getStaticBoxContext().getInputCount()).c_str() + ","
|
||||
+ std::to_string(this->getStaticBoxContext().getOutputCount()).c_str() + ");";
|
||||
if (!checkFailureRoutine(engEvalString(MATLAB_ENGINE, cmd) == 0, "An error occurred while calling OV_createBoxInstance.m")) { return false; }
|
||||
|
||||
//First call to a function of the openvibe toolbox
|
||||
// if it fails, the toolbox may be not installed
|
||||
mxArray* box = engGetVariable(MATLAB_ENGINE, m_boxInstanceVariableName.toASCIIString());
|
||||
OV_ERROR_UNLESS_KRF(box, "Failed to create the box instance with OV_createBoxInstance function.", Kernel::ErrorType::BadProcessing);
|
||||
|
||||
m_initializeFunc = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 3);
|
||||
m_processFunc = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 4);
|
||||
m_uninitializeFunc = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 5);
|
||||
|
||||
// Check that the files actually exist
|
||||
CString filename;
|
||||
filename = workingDir + m_initializeFunc + ".m";
|
||||
OV_ERROR_UNLESS_KRF(FS::Files::fileExists(filename.toASCIIString()), "Cannot open '" << filename << "'", Kernel::ErrorType::BadFileRead);
|
||||
filename = workingDir + m_processFunc + ".m";
|
||||
OV_ERROR_UNLESS_KRF(FS::Files::fileExists(filename.toASCIIString()), "Cannot open '" << filename << "'", Kernel::ErrorType::BadFileRead);
|
||||
filename = workingDir + m_uninitializeFunc + ".m";
|
||||
OV_ERROR_UNLESS_KRF(FS::Files::fileExists(filename.toASCIIString()), "Cannot open '" << filename << "'", Kernel::ErrorType::BadFileRead);
|
||||
|
||||
CString names = "{";
|
||||
CString types = "{";
|
||||
CString values = "{";
|
||||
CString tmp;
|
||||
CIdentifier typeID;
|
||||
|
||||
for (size_t i = 6; i < getStaticBoxContext().getSettingCount(); ++i)
|
||||
{
|
||||
// get the setting name
|
||||
getStaticBoxContext().getSettingName(i, tmp);
|
||||
names = names + "'" + tmp + "' ";
|
||||
|
||||
//setting type
|
||||
getStaticBoxContext().getSettingType(i, typeID);
|
||||
std::stringstream ss;
|
||||
ss << typeID.id();
|
||||
types = types + "uint64(" + ss.str().c_str() + ") ";
|
||||
|
||||
//setting value, typed
|
||||
|
||||
tmp = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), i);
|
||||
if (typeID == OV_TypeId_Stimulation)
|
||||
{
|
||||
uint64_t code = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), i);
|
||||
std::stringstream ss1;
|
||||
ss1 << code;
|
||||
values = values + CString(ss1.str().c_str()) + CString(" ");
|
||||
// we keep the stimulation codes as doubles, to be able to put them in arrays with other doubles (such as timings).
|
||||
// they are still comparable with uint64_t matlab values.
|
||||
}
|
||||
else
|
||||
{
|
||||
if (typeID == OV_TypeId_Stimulation || typeID == OV_TypeId_Boolean || typeID == OV_TypeId_Integer || typeID == OV_TypeId_Float)
|
||||
{
|
||||
values = values + tmp + " "; // we store the value, these types are readable by matlab directly
|
||||
}
|
||||
else { values = values + "'" + tmp + "' "; } // we store them as matlab strings using '
|
||||
}
|
||||
}
|
||||
names += "}";
|
||||
types += "}";
|
||||
values += "}";
|
||||
|
||||
// On Windows, Matlab doesn't sometimes notice .m files have been changed, esp. if you have matlab box running while you change them
|
||||
if (!checkFailureRoutine(engEvalString(MATLAB_ENGINE, "clear functions;") == 0, "An error occurred while calling matlab 'clear functions;'\n"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
cmd = m_boxInstanceVariableName + " = OV_setSettings(" + m_boxInstanceVariableName + "," + names + "," + types + "," + values + ");";
|
||||
if (!checkFailureRoutine(engEvalString(MATLAB_ENGINE, cmd.toASCIIString()) == 0, "Error calling [OV_setSettings]\n")) { return false; }
|
||||
|
||||
// we set the box clock frequency in the box structure, so it's accessible in the user scripts if needed
|
||||
tmp = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
cmd = m_boxInstanceVariableName + ".clock_frequency = " + tmp + ";";
|
||||
if (!checkFailureRoutine(engEvalString(MATLAB_ENGINE, cmd) == 0, "An error occurred while setting the clock frequency\n")) { return false; }
|
||||
|
||||
cmd = m_boxInstanceVariableName + " = " + m_initializeFunc + "(" + m_boxInstanceVariableName + ");";
|
||||
if (!checkFailureRoutine(engEvalString(MATLAB_ENGINE, cmd) == 0, "An error occurred while calling the initialize function\n")) { return false; }
|
||||
|
||||
m_helper->setMatlabEngine(MATLAB_ENGINE);
|
||||
m_helper->setBoxInstanceVariableName(m_boxInstanceVariableName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatlabScripting::closeMatlabEngineSafely()
|
||||
{
|
||||
if (MATLAB_ENGINE == nullptr) { return true; }
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Trying to close Matlab engine\n";
|
||||
#if defined TARGET_OS_Windows
|
||||
__try
|
||||
{
|
||||
#endif
|
||||
if (MATLAB_ENGINE) { if (engClose(MATLAB_ENGINE) != 0) { this->getLogManager() << Kernel::LogLevel_ImportantWarning << "Could not close Matlab engine.\n"; } }
|
||||
#if defined TARGET_OS_Windows
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Closing MATLAB engine failed.\n"
|
||||
<< "\tTo use this box you must have MATLAB (32 bits version) installed on your computer.\n";
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatlabScripting::uninitialize()
|
||||
{
|
||||
delete m_helper;
|
||||
if (MATLAB_ENGINE != nullptr)
|
||||
{
|
||||
CString cmd = m_boxInstanceVariableName + " = " + m_uninitializeFunc + "(" + m_boxInstanceVariableName + ");";
|
||||
if (!checkFailureRoutine(engEvalString(MATLAB_ENGINE, cmd) == 0, "An error occurred while calling the uninitialize function\n")
|
||||
) { } // NOP, we still want to deallocate below
|
||||
// Remove the box from the matlab workspace
|
||||
cmd = "clear " + m_boxInstanceVariableName + ";";
|
||||
if (!checkFailureRoutine(engEvalString(MATLAB_ENGINE, cmd) == 0, "An error occurred while clearing the box\n")
|
||||
) { } // NOP, we still want to deallocate below
|
||||
}
|
||||
|
||||
closeMatlabEngineSafely();
|
||||
if (m_matlabBuffer)
|
||||
{
|
||||
delete[] m_matlabBuffer;
|
||||
m_matlabBuffer = nullptr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_decoders.size(); ++i)
|
||||
{
|
||||
m_decoders[i]->uninitialize();
|
||||
delete m_decoders[i];
|
||||
}
|
||||
for (size_t i = 0; i < m_encoders.size(); ++i)
|
||||
{
|
||||
m_encoders[i]->uninitialize();
|
||||
delete m_encoders[i];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatlabScripting::processClock(Kernel::CMessageClock& /*msg*/)
|
||||
{
|
||||
if (!MATLAB_ENGINE) { return true; }
|
||||
const std::string cmd = std::string(m_boxInstanceVariableName) + ".clock = " + std::to_string(CTime(this->getPlayerContext().getCurrentTime()).toSeconds())
|
||||
+ ";";
|
||||
OV_ERROR_UNLESS_KRF(engEvalString(MATLAB_ENGINE, cmd.c_str()) == 0, "An error occurred while updating the box clock.", Kernel::ErrorType::BadProcessing);
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatlabScripting::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
// In openvibe, pointers to managers can change between the calls to process(), so we need to provide current ones every time
|
||||
m_helper->setManagers(&this->getLogManager(), &this->getErrorManager());
|
||||
|
||||
for (size_t i = 0; i < getStaticBoxContext().getInputCount(); ++i)
|
||||
{
|
||||
for (size_t j = 0; j < boxContext.getInputChunkCount(i); ++j)
|
||||
{
|
||||
m_decoders[i]->decode(j);
|
||||
|
||||
CIdentifier type;
|
||||
getStaticBoxContext().getInputType(i, type);
|
||||
bool unknownStream = true;
|
||||
bool receivedSomething = false;
|
||||
|
||||
if (m_decoders[i]->isHeaderReceived())
|
||||
{
|
||||
receivedSomething = true;
|
||||
// this->getLogManager() << Kernel::LogLevel_Debug << "Received header\n";
|
||||
|
||||
if (type == OV_TypeId_StreamedMatrix)
|
||||
{
|
||||
CMatrix* matrix = static_cast<Toolkit::TStreamedMatrixDecoder<CBoxAlgorithmMatlabScripting>*>(m_decoders[i])->getOutputMatrix();
|
||||
if (!checkFailureRoutine(m_helper->setStreamedMatrixInputHeader(i, matrix), "Error calling [OV_setStreamMatrixInputHeader]\n"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_nInputHeaderSent++;
|
||||
unknownStream = false;
|
||||
}
|
||||
|
||||
if (type == OV_TypeId_Signal)
|
||||
{
|
||||
CMatrix* matrix = static_cast<Toolkit::TSignalDecoder<CBoxAlgorithmMatlabScripting>*>(m_decoders[i])->getOutputMatrix();
|
||||
const uint64_t frequency = static_cast<Toolkit::TSignalDecoder<CBoxAlgorithmMatlabScripting>*>(m_decoders[i])->getOutputSamplingRate();
|
||||
if (!checkFailureRoutine(m_helper->setSignalInputHeader(i, matrix, frequency),
|
||||
"Error calling [OV_setSignalInputHeader]\n")) { return false; }
|
||||
|
||||
m_nInputHeaderSent++;
|
||||
unknownStream = false;
|
||||
}
|
||||
|
||||
if (type == OV_TypeId_FeatureVector)
|
||||
{
|
||||
CMatrix* matrix = static_cast<Toolkit::TFeatureVectorDecoder<CBoxAlgorithmMatlabScripting>*>(m_decoders[i])->getOutputMatrix();
|
||||
if (!checkFailureRoutine(m_helper->setFeatureVectorInputHeader(i, matrix),
|
||||
"Error calling [OV_setFeatureVectorInputHeader]\n")) { return false; }
|
||||
|
||||
m_nInputHeaderSent++;
|
||||
unknownStream = false;
|
||||
}
|
||||
|
||||
if (type == OV_TypeId_Spectrum)
|
||||
{
|
||||
CMatrix* matrix = static_cast<Toolkit::TSpectrumDecoder<CBoxAlgorithmMatlabScripting>*>(m_decoders[i])->getOutputMatrix();
|
||||
CMatrix* freqAbscissa = static_cast<Toolkit::TSpectrumDecoder<CBoxAlgorithmMatlabScripting>*>(m_decoders[i])->getOutputFrequencyAbscissa();
|
||||
const uint64_t frequency = static_cast<Toolkit::TSpectrumDecoder<CBoxAlgorithmMatlabScripting>*>(m_decoders[i])->getOutputSamplingRate();
|
||||
|
||||
if (!checkFailureRoutine(m_helper->setSpectrumInputHeader(i, matrix, freqAbscissa, frequency),
|
||||
"Error calling [OV_setSpectrumInputHeader]\n")) { return false; }
|
||||
|
||||
m_nInputHeaderSent++;
|
||||
unknownStream = false;
|
||||
}
|
||||
|
||||
if (type == OV_TypeId_ChannelLocalisation)
|
||||
{
|
||||
CMatrix* matrix = static_cast<Toolkit::TChannelLocalisationDecoder<CBoxAlgorithmMatlabScripting>*>(m_decoders[i])->getOutputMatrix();
|
||||
const bool dynamic = static_cast<Toolkit::TChannelLocalisationDecoder<CBoxAlgorithmMatlabScripting>*>(m_decoders[i])->getOutputDynamic();
|
||||
if (!checkFailureRoutine(m_helper->setChannelLocalisationInputHeader(i, matrix, dynamic),
|
||||
"Error calling [OV_setChannelLocalizationInputHeader]\n")) { return false; }
|
||||
|
||||
m_nInputHeaderSent++;
|
||||
unknownStream = false;
|
||||
}
|
||||
|
||||
if (type == OV_TypeId_ExperimentInfo)
|
||||
{
|
||||
OV_WARNING_K(
|
||||
"The Experiment Information Stream is not implemented with the Matlab Scripting Box. If this is relevant for your usage, please contact the official development Team.")
|
||||
;
|
||||
m_nInputHeaderSent++;
|
||||
unknownStream = false;
|
||||
}
|
||||
|
||||
if (type == OV_TypeId_Stimulations)
|
||||
{
|
||||
if (!checkFailureRoutine(m_helper->setStimulationsInputHeader(i), "Error calling [OV_setStimulationsInputHeader]\n")) { return false; }
|
||||
|
||||
m_nInputHeaderSent++;
|
||||
unknownStream = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_decoders[i]->isBufferReceived())
|
||||
{
|
||||
// this->getLogManager() << Kernel::LogLevel_Debug << "Received buffer\n";
|
||||
receivedSomething = true;
|
||||
//
|
||||
if (type == OV_TypeId_StreamedMatrix || this->getTypeManager().isDerivedFromStream(type, OV_TypeId_StreamedMatrix))
|
||||
{
|
||||
CMatrix* matrix = static_cast<Toolkit::TSignalDecoder<CBoxAlgorithmMatlabScripting>*>(m_decoders[i])->getOutputMatrix();
|
||||
const uint64_t start = boxContext.getInputChunkStartTime(i, j);
|
||||
const uint64_t end = boxContext.getInputChunkEndTime(i, j);
|
||||
if (!checkFailureRoutine(m_helper->addStreamedMatrixInputBuffer(i, matrix, start, end),
|
||||
"Error calling [OV_addInputBuffer (Streamed Matrix or child stream)]\n")) { return false; }
|
||||
unknownStream = false;
|
||||
}
|
||||
|
||||
if (type == OV_TypeId_Stimulations)
|
||||
{
|
||||
IStimulationSet* stimSet = static_cast<Toolkit::TStimulationDecoder<CBoxAlgorithmMatlabScripting>*>(m_decoders[i])->getOutputStimulationSet();
|
||||
const uint64_t start = boxContext.getInputChunkStartTime(i, j);
|
||||
const uint64_t end = boxContext.getInputChunkEndTime(i, j);
|
||||
if (stimSet->getStimulationCount() > 0)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Inserting stimulation set with size " << stimSet->getStimulationCount() << "\n";
|
||||
}
|
||||
if (!checkFailureRoutine(m_helper->addStimulationsInputBuffer(i, stimSet, start, end),
|
||||
"Error calling [OV_addInputBuffer (Stimulations)]\n")) { return false; }
|
||||
unknownStream = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_decoders[i]->isEndReceived())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "Received end\n";
|
||||
receivedSomething = true;
|
||||
unknownStream = false;
|
||||
// @FIXME should something additional be done here?
|
||||
}
|
||||
|
||||
OV_ERROR_UNLESS_KRF(!receivedSomething || !unknownStream, "Unknown Stream Type " << type.str() << " on input [" << i << "].",
|
||||
Kernel::ErrorType::BadProcessing);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_nInputHeaderSent < getStaticBoxContext().getInputCount())
|
||||
{
|
||||
// not ready to process
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// CALL TO PROCESS FUNCTION
|
||||
std::string command = std::string(m_boxInstanceVariableName.toASCIIString()) + " = " + m_processFunc.toASCIIString() + "("
|
||||
+ m_boxInstanceVariableName.toASCIIString() + ");";
|
||||
if (!checkFailureRoutine(engEvalString(MATLAB_ENGINE, command.c_str()) == 0, "Error calling the Process function.\n")) { return false; }
|
||||
|
||||
// Go through every output in the matlab box and copy the data to the C++ side
|
||||
for (size_t i = 0; i < getStaticBoxContext().getOutputCount(); ++i)
|
||||
{
|
||||
// now we check for pending output chunk to be sent (output type independent call)
|
||||
command = std::string("OV_PENDING_COUNT_TMP = OV_getNbPendingOutputChunk(") + m_boxInstanceVariableName.toASCIIString() + ", "
|
||||
+ std::to_string(i + 1) + ");";
|
||||
if (!checkFailureRoutine(engEvalString(MATLAB_ENGINE, command.c_str()) == 0, "Error calling [OV_getNbPendingOutputChunk].\n")) { return false; }
|
||||
|
||||
mxArray* pending = engGetVariable(MATLAB_ENGINE, "OV_PENDING_COUNT_TMP");
|
||||
const double dPending = *mxGetPr(pending);
|
||||
|
||||
CIdentifier typeID;
|
||||
getStaticBoxContext().getOutputType(i, typeID);
|
||||
|
||||
for (size_t c = 0; c < size_t(dPending); ++c)
|
||||
{
|
||||
// If no header were ever sent, we need to extract header information in the matlab box
|
||||
// This header must have been set prior to sending the very first buffer.
|
||||
// @FIXME the practice used below of assigning to getters is nasty, it should be refactored to e.g. using getter/setter pairs
|
||||
if (!m_oHeaderStates[i])
|
||||
{
|
||||
bool unknownType = true;
|
||||
if (typeID == OV_TypeId_StreamedMatrix)
|
||||
{
|
||||
CMatrix* matrixToSend = static_cast<Toolkit::TStreamedMatrixEncoder<CBoxAlgorithmMatlabScripting>*>(m_encoders[i])->getInputMatrix();
|
||||
if (!checkFailureRoutine(m_helper->getStreamedMatrixOutputHeader(i, matrixToSend),
|
||||
"Error calling [OV_getStreamedMatrixOutputHeader]. Did you correctly set the output header in the matlab structure ?\n")
|
||||
) { return false; }
|
||||
|
||||
unknownType = false;
|
||||
}
|
||||
else if (typeID == OV_TypeId_Signal)
|
||||
{
|
||||
CMatrix* matrixToSend = static_cast<Toolkit::TSignalEncoder<CBoxAlgorithmMatlabScripting>*>(m_encoders[i])->getInputMatrix();
|
||||
uint64_t sampling = static_cast<Toolkit::TSignalEncoder<CBoxAlgorithmMatlabScripting>*>(m_encoders[i])->getInputSamplingRate();
|
||||
if (!checkFailureRoutine(m_helper->getSignalOutputHeader(i, matrixToSend, sampling),
|
||||
"Error calling [OV_getSignalOutputHeader]. Did you correctly set the output header in the matlab structure ?\n")
|
||||
) { return false; }
|
||||
|
||||
// Set the new sampling rate
|
||||
static_cast<Toolkit::TSignalEncoder<CBoxAlgorithmMatlabScripting>*>(m_encoders[i])->getInputSamplingRate() = sampling;
|
||||
|
||||
unknownType = false;
|
||||
}
|
||||
else if (typeID == OV_TypeId_FeatureVector)
|
||||
{
|
||||
CMatrix* matrixToSend = static_cast<Toolkit::TFeatureVectorEncoder<CBoxAlgorithmMatlabScripting>*>(m_encoders[i])->getInputMatrix();
|
||||
if (!checkFailureRoutine(m_helper->getFeatureVectorOutputHeader(i, matrixToSend),
|
||||
"Error calling [OV_getFeatureVectorOutputHeader]. Did you correctly set the output header in the matlab structure ?\n")
|
||||
) { return false; }
|
||||
|
||||
unknownType = false;
|
||||
}
|
||||
else if (typeID == OV_TypeId_Spectrum)
|
||||
{
|
||||
CMatrix* matrixToSend = static_cast<Toolkit::TSpectrumEncoder<CBoxAlgorithmMatlabScripting>*>(m_encoders[i])->getInputMatrix();
|
||||
CMatrix* freqAbscissa = static_cast<Toolkit::TSpectrumEncoder<CBoxAlgorithmMatlabScripting>*>(m_encoders[i])->getInputFrequencyAbscissa();
|
||||
uint64_t frequency = static_cast<Toolkit::TSpectrumEncoder<CBoxAlgorithmMatlabScripting>*>(m_encoders[i])->getInputSamplingRate();
|
||||
|
||||
if (!checkFailureRoutine(m_helper->getSpectrumOutputHeader(i, matrixToSend, freqAbscissa, frequency),
|
||||
"Error calling [OV_getSpectrumOutputHeader]. Did you correctly set the output header in the matlab structure ?\n")
|
||||
) { return false; }
|
||||
|
||||
unknownType = false;
|
||||
}
|
||||
else if (typeID == OV_TypeId_ChannelLocalisation)
|
||||
{
|
||||
CMatrix* matrixToSend = static_cast<Toolkit::TChannelLocalisationEncoder<CBoxAlgorithmMatlabScripting>*>(m_encoders[i])->getInputMatrix();
|
||||
bool dynamic = static_cast<Toolkit::TChannelLocalisationEncoder<CBoxAlgorithmMatlabScripting>*>(m_encoders[i])->getInputDynamic();
|
||||
if (!checkFailureRoutine(m_helper->getChannelLocalisationOutputHeader(i, matrixToSend, dynamic),
|
||||
"Error calling [OV_getChannelLocalizationOutputHeader]. Did you correctly set the output header in the matlab structure ?\n")
|
||||
) { return false; }
|
||||
|
||||
// Set the new channel localisation
|
||||
static_cast<Toolkit::TChannelLocalisationEncoder<CBoxAlgorithmMatlabScripting>*>(m_encoders[i])->getInputDynamic() = dynamic;
|
||||
|
||||
unknownType = false;
|
||||
}
|
||||
else if (typeID == OV_TypeId_ExperimentInfo)
|
||||
{
|
||||
OV_WARNING_K(
|
||||
"The Experiment Information Stream is not implemented with the Matlab Scripting Box. If this is relevant for your usage, please contact the official development Team.")
|
||||
;
|
||||
unknownType = false;
|
||||
}
|
||||
else if (typeID == OV_TypeId_Stimulations)
|
||||
{
|
||||
IStimulationSet* stimSet = static_cast<Toolkit::TStimulationEncoder<CBoxAlgorithmMatlabScripting>*>(m_encoders[i])->getInputStimulationSet();
|
||||
if (!checkFailureRoutine(m_helper->getStimulationsOutputHeader(i, stimSet),
|
||||
"Error calling [OV_getStimulationsOutputHeader]. Did you correctly set the output header in the matlab structure ?\n")
|
||||
) { return false; }
|
||||
|
||||
unknownType = false;
|
||||
}
|
||||
|
||||
OV_ERROR_UNLESS_KRF(!unknownType, "Unknown Stream Type on output [" << i << "].", Kernel::ErrorType::BadProcessing);
|
||||
|
||||
m_encoders[i]->encodeHeader();
|
||||
boxContext.markOutputAsReadyToSend(i, 0, 0);
|
||||
|
||||
m_oHeaderStates[i] = true;
|
||||
}
|
||||
|
||||
|
||||
bool unknownType = true;
|
||||
uint64_t start = 0;
|
||||
uint64_t end = 0;
|
||||
|
||||
if (typeID == OV_TypeId_StreamedMatrix || this->getTypeManager().isDerivedFromStream(typeID, OV_TypeId_StreamedMatrix))
|
||||
{
|
||||
CMatrix* matrixToSend = static_cast<Toolkit::TStreamedMatrixEncoder<CBoxAlgorithmMatlabScripting>*>(m_encoders[i])->getInputMatrix();
|
||||
if (!checkFailureRoutine(m_helper->popStreamedMatrixOutputBuffer(i, matrixToSend, start, end),
|
||||
"Error calling [OV_popOutputBufferReshape] for Streamed Matrix stream or child stream.\n")) { return false; }
|
||||
|
||||
unknownType = false;
|
||||
}
|
||||
|
||||
if (typeID == OV_TypeId_Stimulations)
|
||||
{
|
||||
IStimulationSet* stimSet = static_cast<Toolkit::TStimulationEncoder<CBoxAlgorithmMatlabScripting>*>(m_encoders[i])->getInputStimulationSet();
|
||||
stimSet->clear();
|
||||
if (!checkFailureRoutine(m_helper->popStimulationsOutputBuffer(i, stimSet, start, end),
|
||||
"Error calling [OV_popOutputBuffer] for Stimulation stream.\n")) { return false; }
|
||||
unknownType = false;
|
||||
}
|
||||
|
||||
OV_ERROR_UNLESS_KRF(!unknownType, "Unknown Stream Type on output [" << i << "].", Kernel::ErrorType::BadProcessing);
|
||||
|
||||
m_encoders[i]->encodeBuffer();
|
||||
boxContext.markOutputAsReadyToSend(i, start, end);
|
||||
}
|
||||
mxDestroyArray(pending);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatlabScripting::processInput(const size_t /*index*/)
|
||||
{
|
||||
OV_ERROR_UNLESS_KRF(getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess(),
|
||||
"Error while marking algorithm as ready to process", Kernel::ErrorType::Internal);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatlabScripting::printOutputBufferWithFormat()
|
||||
{
|
||||
// the buffer for the console
|
||||
|
||||
// @FIXME allow seeing more of the error message!
|
||||
|
||||
std::stringstream ss;
|
||||
ss << m_matlabBuffer;
|
||||
if (!ss.str().empty())
|
||||
{
|
||||
const size_t errIdx1 = ss.str().find("??? ");
|
||||
const size_t errIdx2 = ss.str().find("Error: ");
|
||||
const size_t errIdx3 = ss.str().find("Error ");
|
||||
|
||||
// Find the earliest error message
|
||||
size_t errIdx = std::string::npos;
|
||||
if (errIdx == std::string::npos || (errIdx1 != std::string::npos && errIdx1 < errIdx)) { errIdx = errIdx1; }
|
||||
if (errIdx == std::string::npos || (errIdx2 != std::string::npos && errIdx2 < errIdx)) { errIdx = errIdx2; }
|
||||
if (errIdx == std::string::npos || (errIdx3 != std::string::npos && errIdx3 < errIdx)) { errIdx = errIdx3; }
|
||||
|
||||
size_t warnIdx = ss.str().find("Warning: ");
|
||||
if (errIdx == std::string::npos && warnIdx == std::string::npos)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "\n---- MATLAB BUFFER - INFO ----\n" << ss.str().substr(0, (warnIdx < errIdx) ? warnIdx : errIdx) << "\n";
|
||||
}
|
||||
if (warnIdx != std::string::npos)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Warning << "\n---- MATLAB BUFFER - WARNING ----\n" << ss.str().substr(warnIdx, errIdx) << "\n";
|
||||
}
|
||||
if (errIdx != std::string::npos)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "\n---- MATLAB BUFFER - ERROR ! ----\n" << ss.str().substr(errIdx) << "\n";
|
||||
m_errorDetected = true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Matlab
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyMatlab
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyMatlab
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include "../ovpCMatlabHelper.h"
|
||||
#include <map>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Matlab {
|
||||
class CBoxAlgorithmMatlabScripting final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
void release() override { delete this; }
|
||||
uint64_t getClockFrequency() override { return m_clockFrequency << 32; }
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processClock(Kernel::CMessageClock& msg) override;
|
||||
bool process() override;
|
||||
bool processInput(const size_t index) override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_MatlabScripting)
|
||||
|
||||
private:
|
||||
bool openMatlabEngineSafely();
|
||||
bool closeMatlabEngineSafely();
|
||||
|
||||
uint64_t m_clockFrequency = 0;
|
||||
|
||||
std::map<size_t, Toolkit::TDecoder<CBoxAlgorithmMatlabScripting>*> m_decoders;
|
||||
size_t m_nInputHeaderSent = 0;
|
||||
|
||||
std::map<size_t, Toolkit::TEncoder<CBoxAlgorithmMatlabScripting>*> m_encoders;
|
||||
std::map<size_t, bool> m_oHeaderStates;
|
||||
|
||||
void* m_engineHandle = nullptr;
|
||||
CString m_boxInstanceVariableName; //must be unique
|
||||
|
||||
|
||||
CMatlabHelper* m_helper = nullptr;
|
||||
|
||||
CString m_matlabPath; // On Linux, path of the executable. On Windows, the executable directory.
|
||||
CString m_processFunc;
|
||||
CString m_initializeFunc;
|
||||
CString m_uninitializeFunc;
|
||||
|
||||
char* m_matlabBuffer = nullptr;
|
||||
bool m_errorDetected = false;
|
||||
bool printOutputBufferWithFormat();
|
||||
bool checkFailureRoutine(bool result, const CString& msg);
|
||||
static void sanitizePath(CString& pathToModify);
|
||||
|
||||
//void sendOutputHeader(CIdentifier idOutputType);
|
||||
//void sendOutputBuffer(CIdentifier idOutputType);
|
||||
};
|
||||
|
||||
class CBoxAlgorithmMatlabScriptingListener final : public Toolkit::TBoxListener<IBoxListener>
|
||||
{
|
||||
public:
|
||||
bool onSettingTypeChanged(Kernel::IBox& box, const size_t index) override
|
||||
{
|
||||
//we must have the first STATIC_SETTINGS_COUNT settings
|
||||
if (index < 6)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Warning << "One of the predefined setting has changed type, falling back to default.\n";
|
||||
box.setSettingType(index, OV_TypeId_String); // they are all strings
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool onSettingValueChanged(Kernel::IBox& box, const size_t index) override
|
||||
{
|
||||
//we must have the first STATIC_SETTINGS_COUNT settings
|
||||
|
||||
CString str;
|
||||
box.getSettingValue(0, str);
|
||||
const uint64_t value = atoi(str.toASCIIString());
|
||||
if (index == 0 && (value < 1 || value > 128))
|
||||
{
|
||||
OV_WARNING_K("Clock Frequency must be an integer between 1 and 128 Hz. Falling back to default.");
|
||||
box.setSettingValue(index, "64");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
|
||||
};
|
||||
|
||||
class CBoxAlgorithmMatlabScriptingDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
void release() override { }
|
||||
CString getName() const override { return CString("Matlab Scripting"); }
|
||||
CString getAuthorName() const override { return CString("L. Bonnet"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INRIA"); }
|
||||
|
||||
CString getShortDescription() const override { return CString("Executes matlab scripts. To be used correctly, you must have Matlab installed."); }
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return CString(
|
||||
"User must implement the matlab functions:\n[box_out]=bci_Initialize(box_in)\n[box_out]=bci_Process(box_in)\n[box_out]=bci_Uninitialize(box_in)\nPlease refer to the dedicated documentation <openvibe.inria.fr/tutorial-using-matlab-with-openvibe> for more information.");
|
||||
}
|
||||
|
||||
CString getCategory() const override { return CString("Scripting"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getStockItemName() const override { return CString("gtk-execute"); }
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_MatlabScripting; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmMatlabScripting; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addSetting("Box clock frequency in Hz", OV_TypeId_Integer, "64");
|
||||
|
||||
#if defined TARGET_OS_Linux
|
||||
prototype.addSetting("Matlab executable (path)", OV_TypeId_Filename, "[ssh user@host] /path/to/matlab");
|
||||
#elif defined TARGET_OS_Windows
|
||||
// On Windows, the executable is not actually used, but its path is parsed from it and added to PATH.
|
||||
// Background: Matlab's engOpen() takes a different argument on Windows and on Linux. On Windows, we need to have Matlab
|
||||
// on PATH, in Linux we need to provide a full path of the executable. However, we'd like our example scenarios to give
|
||||
// correct instructions to the user on both platforms, hence we use a setting that contains the information for both use-cases.
|
||||
prototype.addSetting("Matlab executable (path)", OV_TypeId_Filename, "C:/Program Files (x86)/MATLAB/R2013b/bin/win32/matlab.exe");
|
||||
#else
|
||||
#endif
|
||||
prototype.addSetting("Matlab working directory", OV_TypeId_String, "${Player_ScenarioDirectory}");
|
||||
prototype.addSetting("Initialize function", OV_TypeId_String, "matlab_Initialize");
|
||||
prototype.addSetting("Process function", OV_TypeId_String, "matlab_Process");
|
||||
prototype.addSetting("Uninitialize function", OV_TypeId_String, "matlab_Uninitialize");
|
||||
|
||||
prototype.addFlag(Kernel::BoxFlag_CanAddInput);
|
||||
prototype.addFlag(Kernel::BoxFlag_CanModifyInput);
|
||||
prototype.addFlag(Kernel::BoxFlag_CanAddOutput);
|
||||
prototype.addFlag(Kernel::BoxFlag_CanModifyOutput);
|
||||
prototype.addFlag(Kernel::BoxFlag_CanAddSetting);
|
||||
prototype.addFlag(Kernel::BoxFlag_CanModifySetting);
|
||||
|
||||
//prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmMatlabScriptingListener; }
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_MatlabScriptingDesc)
|
||||
};
|
||||
} // namespace Matlab
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyMatlab
|
||||
+493
@@ -0,0 +1,493 @@
|
||||
#if defined TARGET_HAS_ThirdPartyMatlab
|
||||
|
||||
#include "ovpCMatlabHelper.h"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <mex.h>
|
||||
//#include <engine.h>
|
||||
#include <string>
|
||||
|
||||
#if defined TARGET_OS_Windows
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Matlab {
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------
|
||||
static std::string escapeMatlabString(const char* sStringToEscape)
|
||||
{
|
||||
std::string str = std::string(sStringToEscape);
|
||||
auto it = str.begin();
|
||||
while (it != str.end())
|
||||
{
|
||||
if (*it == '\'')
|
||||
{
|
||||
str.insert(it, '\'');
|
||||
++it;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
static std::string genLabelsList(CMatrix* matrix, const size_t axis = 0)
|
||||
{
|
||||
std::string labelsList;
|
||||
for (size_t i = 0; i < matrix->getDimensionSize(axis); ++i)
|
||||
{
|
||||
labelsList += std::string("'") + escapeMatlabString(matrix->getDimensionLabel(axis, i)) + "' ";
|
||||
}
|
||||
return labelsList;
|
||||
}
|
||||
|
||||
|
||||
static uint64_t convertFromMArray(mxArray* array, const size_t index)
|
||||
{
|
||||
const double* ptr = ::mxGetPr(array);
|
||||
const double timeValue = double(ptr[index]);
|
||||
return CTime(timeValue).time();
|
||||
}
|
||||
|
||||
static uint64_t castFromMArray(mxArray* array, const size_t index)
|
||||
{
|
||||
const double* ptr = ::mxGetPr(array);
|
||||
const double value = double(ptr[index]);
|
||||
return uint64_t(value);
|
||||
}
|
||||
|
||||
static CString getNameFromCell(mxArray* names, const size_t index)
|
||||
{
|
||||
mxArray* cell = mxGetCell(names, index);
|
||||
if (!cell) { return CString(""); }
|
||||
const mwSize* cellSizes = mxGetDimensions(cell);
|
||||
char* name = new char[cellSizes[1] + 1];
|
||||
name[cellSizes[1]] = '\0';
|
||||
for (size_t cellsize = 0; cellsize < size_t(cellSizes[1]); ++cellsize) { name[cellsize] = static_cast<char*>(mxGetData(cell))[cellsize * 2]; }
|
||||
CString res = name;
|
||||
delete name;
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<CString> CMatlabHelper::getNamelist(const char* name) const
|
||||
{
|
||||
std::vector<CString> res;
|
||||
|
||||
mxArray* marray = engGetVariable(m_matlabEngine, name);
|
||||
if (!marray)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Nonexisting variable [" << name << "]\n";
|
||||
return res;
|
||||
}
|
||||
|
||||
const mwSize nbCells = mxGetNumberOfElements(marray);
|
||||
for (size_t cell = 0; cell < nbCells; ++cell) { res.push_back(getNameFromCell(marray, cell)); }
|
||||
|
||||
mxDestroyArray(marray);
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t CMatlabHelper::getUi32FromEnv(const char* name) const
|
||||
{
|
||||
mxArray* marray = engGetVariable(m_matlabEngine, name);
|
||||
if (!marray)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Nonexisting variable [" << name << "]\n";
|
||||
return 0;
|
||||
}
|
||||
const uint32_t res = uint32_t(*mxGetPr(marray));
|
||||
mxDestroyArray(marray);
|
||||
return res;
|
||||
}
|
||||
|
||||
uint64_t CMatlabHelper::getUi64FromEnv(const char* name) const
|
||||
{
|
||||
mxArray* marray = engGetVariable(m_matlabEngine, name);
|
||||
if (!marray)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Nonexisting variable [" << name << "]\n";
|
||||
return 0;
|
||||
}
|
||||
const uint64_t res = uint64_t(*mxGetPr(marray));
|
||||
mxDestroyArray(marray);
|
||||
return res;
|
||||
}
|
||||
|
||||
uint64_t CMatlabHelper::genUi64FromEnvConverted(const char* name) const
|
||||
{
|
||||
mxArray* marray = engGetVariable(m_matlabEngine, name);
|
||||
if (!marray)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Nonexisting variable [" << name << "]\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
const double value = double(*mxGetPr(marray));
|
||||
const uint64_t res = CTime(value).time();
|
||||
mxDestroyArray(marray);
|
||||
return res;
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
bool CMatlabHelper::setStreamedMatrixInputHeader(const size_t index, CMatrix* matrix) const
|
||||
{
|
||||
std::string labelList;
|
||||
std::string dimensionSizes;
|
||||
for (size_t dim = 0; dim < matrix->getDimensionCount(); ++dim)
|
||||
{
|
||||
dimensionSizes += std::to_string(matrix->getDimensionSize(dim)) + " ";
|
||||
labelList += std::string("{") + genLabelsList(matrix, dim) + "} ";
|
||||
}
|
||||
//function box_out = OV_setStreamedMatrixInputInputHeader(box_in, input_index, dimension_count, dimension_sizes, dimension_labels)
|
||||
|
||||
const std::string cmd = std::string(m_boxInstanceVariableName) + " = OV_setStreamedMatrixInputHeader(" + m_boxInstanceVariableName.toASCIIString() + ","
|
||||
+ std::to_string(index + 1) + "," + std::to_string(matrix->getDimensionCount()) + "," + "[" + dimensionSizes + "],"
|
||||
+ "{" + labelList + "});";
|
||||
|
||||
return engEvalString(m_matlabEngine, cmd.c_str()) == 0;
|
||||
}
|
||||
|
||||
bool CMatlabHelper::setFeatureVectorInputHeader(const size_t index, CMatrix* matrix) const
|
||||
{
|
||||
const std::string labelList = genLabelsList(matrix, 0);
|
||||
|
||||
//function box_out = OV_setStreamedMatrixInputInputHeader(box_in, input_index, dimension_count, dimension_sizes, dimension_labels)
|
||||
|
||||
//box_out = OV_setFeatureVectorInputHeader(box_in, input_index, nb_features, labels)
|
||||
const std::string cmd = std::string(m_boxInstanceVariableName.toASCIIString()) + " = OV_setFeatureVectorInputHeader("
|
||||
+ m_boxInstanceVariableName.toASCIIString() + "," + std::to_string(index + 1) + ","
|
||||
+ std::to_string(matrix->getDimensionSize(0)) + "," + "{" + labelList + "});";
|
||||
|
||||
return engEvalString(m_matlabEngine, cmd.c_str()) == 0;
|
||||
}
|
||||
|
||||
bool CMatlabHelper::setSignalInputHeader(const size_t index, CMatrix* matrix, const uint64_t frequency) const
|
||||
{
|
||||
const std::string labelList = genLabelsList(matrix, 0);
|
||||
//function box_out = ov_set_signal_input_header(box_in, input_index, nb_channel, nb_samples_per_buffer, channel_names, sampling_rate)
|
||||
|
||||
const std::string cmd = std::string(m_boxInstanceVariableName) + " = OV_setSignalInputHeader(" + m_boxInstanceVariableName.toASCIIString() + ","
|
||||
+ std::to_string(index + 1) + "," + std::to_string(matrix->getDimensionSize(0)) + ","
|
||||
+ std::to_string(matrix->getDimensionSize(1)) + "," + "{" + labelList + "}," + std::to_string(frequency) + ");";
|
||||
|
||||
return engEvalString(m_matlabEngine, cmd.c_str()) == 0;
|
||||
}
|
||||
|
||||
bool CMatlabHelper::setChannelLocalisationInputHeader(const size_t index, CMatrix* matrix, const bool dynamic) const
|
||||
{
|
||||
const std::string labelList = genLabelsList(matrix, 0);
|
||||
//function box_out = OV_setChannelLocalisationInputHeader(box_in, input_index, nb_channels, channel_names, dynamic)
|
||||
|
||||
const std::string cmd = std::string(m_boxInstanceVariableName) + " = OV_setChannelLocalisationInputHeader(" + m_boxInstanceVariableName.toASCIIString()
|
||||
+ "," + std::to_string(index + 1) + "," + std::to_string(matrix->getDimensionSize(0)) + "," + "{" + labelList + "},"
|
||||
+ (dynamic ? "true" : "false") + ");";
|
||||
|
||||
return engEvalString(m_matlabEngine, cmd.c_str()) == 0;
|
||||
}
|
||||
|
||||
bool CMatlabHelper::setSpectrumInputHeader(const size_t index, CMatrix* matrix, CMatrix* frequencyAbscissa, const uint64_t frequency) const
|
||||
{
|
||||
const std::string labelList = genLabelsList(matrix, 0);
|
||||
const std::string abscissaList = genLabelsList(matrix, 1);
|
||||
|
||||
// @FIXME CERT this is now one dim array
|
||||
mxArray* matlabMatrix = ::mxCreateDoubleMatrix(frequencyAbscissa->getDimensionSize(0), 1, mxREAL);
|
||||
|
||||
memcpy(mxGetPr(matlabMatrix), frequencyAbscissa->getBuffer(), frequencyAbscissa->getBufferElementCount() * sizeof(double));
|
||||
engPutVariable(m_matlabEngine, "OV_MATRIX_TMP", matlabMatrix);
|
||||
|
||||
mxDestroyArray(matlabMatrix);
|
||||
|
||||
//box_out = OV_setSpectrumInputHeader(box_in, input_index, nb_channels, channel_names, nb_bands, band_names, bands, sampling_rate)
|
||||
const std::string cmd = std::string(m_boxInstanceVariableName.toASCIIString()) + " = OV_setSpectrumInputHeader("
|
||||
+ std::string(m_boxInstanceVariableName.toASCIIString()) + ","
|
||||
+ std::to_string(index + 1) + "," // input_index
|
||||
+ std::to_string(matrix->getDimensionSize(0)) + "," // nb_channels
|
||||
+ "{" + labelList + "}," // channel_names
|
||||
+ std::to_string(matrix->getDimensionSize(1)) + "," // nb_freq abscissa
|
||||
+ "{" + abscissaList + "}," //freq abscissa names
|
||||
+ "OV_MATRIX_TMP," //bands
|
||||
+ std::to_string(frequency) + ");"; //sampling rate
|
||||
|
||||
return engEvalString(m_matlabEngine, cmd.c_str()) == 0;
|
||||
}
|
||||
|
||||
bool CMatlabHelper::setStimulationsInputHeader(const size_t index) const
|
||||
{
|
||||
//box_out = OV_setStimulationInputHeader(box_in, input_index)
|
||||
const std::string cmd = std::string(m_boxInstanceVariableName) + " = OV_setStimulationsInputHeader(" + m_boxInstanceVariableName.toASCIIString() + "," +
|
||||
std::to_string(index + 1) + ");";
|
||||
|
||||
return engEvalString(m_matlabEngine, cmd.c_str()) == 0;
|
||||
}
|
||||
|
||||
//bool setExperimentInfoInputHeader(CMatrix * pMatrix);
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
bool CMatlabHelper::addStreamedMatrixInputBuffer(const size_t index, CMatrix* matrix, const uint64_t startTime, const uint64_t endTime) const
|
||||
{
|
||||
mwSize* dims = new mwSize[matrix->getDimensionCount()];
|
||||
size_t j = matrix->getDimensionCount() - 1;
|
||||
for (size_t i = 0; i < matrix->getDimensionCount(); ++i)
|
||||
{
|
||||
dims[i] = matrix->getDimensionSize(j);
|
||||
j--;
|
||||
}
|
||||
|
||||
mxArray* matlabMatrix = ::mxCreateNumericArray(matrix->getDimensionCount(), dims, mxDOUBLE_CLASS, mxREAL);
|
||||
|
||||
//test : channel 1 samples to '10'
|
||||
//for (size_t i = 0; i < 32;i++) matrix->getBuffer()[i] = 10;
|
||||
|
||||
memcpy(mxGetPr(matlabMatrix), matrix->getBuffer(), matrix->getBufferElementCount() * sizeof(double));
|
||||
engPutVariable(m_matlabEngine, "OV_MATRIX_TMP", matlabMatrix);
|
||||
|
||||
mxDestroyArray(matlabMatrix);
|
||||
|
||||
const std::string cmd = std::string(m_boxInstanceVariableName) + " = OV_addInputBuffer(" + m_boxInstanceVariableName.toASCIIString() + ","
|
||||
+ std::to_string(index + 1) + ","
|
||||
+ std::to_string(CTime(startTime).toSeconds()) + ","
|
||||
+ std::to_string(CTime(endTime).toSeconds()) + ",OV_MATRIX_TMP');";
|
||||
// please note the transpose operator ' to put the matrix with 1 channel per line
|
||||
|
||||
delete[] dims;
|
||||
|
||||
return engEvalString(m_matlabEngine, cmd.c_str()) == 0;
|
||||
}
|
||||
|
||||
bool CMatlabHelper::addStimulationsInputBuffer(const size_t index, IStimulationSet* stimSet, const uint64_t startTime, const uint64_t endTime) const
|
||||
{
|
||||
if (stimSet->getStimulationCount() == 0 && engEvalString(m_matlabEngine, "OV_MATRIX_TMP = 0") != 0) { return false; }
|
||||
// we create a 3xN matrix for N stims (access is easier in that order)
|
||||
mxArray* matrix = ::mxCreateDoubleMatrix(3, size_t(stimSet->getStimulationCount()), mxREAL);
|
||||
|
||||
for (size_t i = 0; i < stimSet->getStimulationCount(); ++i)
|
||||
{
|
||||
::mxGetPr(matrix)[i * 3] = double(stimSet->getStimulationIdentifier(i));
|
||||
::mxGetPr(matrix)[i * 3 + 1] = CTime(stimSet->getStimulationDate(i)).toSeconds();
|
||||
::mxGetPr(matrix)[i * 3 + 2] = CTime(stimSet->getStimulationDuration(i)).toSeconds();
|
||||
}
|
||||
|
||||
engPutVariable(m_matlabEngine, "OV_MATRIX_TMP", matrix);
|
||||
|
||||
mxDestroyArray(matrix);
|
||||
|
||||
const std::string cmd = std::string(m_boxInstanceVariableName) + " = OV_addInputBuffer(" + m_boxInstanceVariableName.toASCIIString() + ","
|
||||
+ std::to_string(index + 1) + ","
|
||||
+ std::to_string(CTime(startTime).toSeconds()) + ","
|
||||
+ std::to_string(CTime(endTime).toSeconds()) + ",OV_MATRIX_TMP');";
|
||||
|
||||
return (engEvalString(m_matlabEngine, cmd.c_str()) == 0);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
bool CMatlabHelper::getStreamedMatrixOutputHeader(const size_t index, CMatrix* matrix) const
|
||||
{
|
||||
const std::string cmd = std::string("[OV_ERRNO, OV_NB_DIMENSIONS, OV_DIMENSION_SIZES, OV_DIMENSION_LABELS] = OV_getStreamedMatrixOutputHeader(")
|
||||
+ m_boxInstanceVariableName.toASCIIString() + "," + std::to_string(index + 1) + ");";
|
||||
OV_ERROR_UNLESS_KRF(engEvalString(m_matlabEngine, cmd.c_str()) == 0 && getUi32FromEnv("OV_ERRNO") == 0,
|
||||
"Could not get Streamed matrix output header", Kernel::ErrorType::BadProcessing);
|
||||
|
||||
const size_t nDimension = getUi32FromEnv("OV_NB_DIMENSIONS");
|
||||
mxArray* dimensionSizes = engGetVariable(m_matlabEngine, "OV_DIMENSION_SIZES");
|
||||
if (!dimensionSizes)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Nonexisting variable [OV_DIMENSION_SIZES]\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<CString> nameList = getNamelist("OV_DIMENSION_LABELS");
|
||||
|
||||
matrix->setDimensionCount(nDimension);
|
||||
size_t idx = 0;
|
||||
for (size_t i = 0; i < nDimension; ++i)
|
||||
{
|
||||
matrix->setDimensionSize(i, size_t(mxGetPr(dimensionSizes)[i]));
|
||||
for (size_t x = 0; x < matrix->getDimensionSize(i) && idx < nameList.size(); ++x)
|
||||
{
|
||||
matrix->setDimensionLabel(i, x, escapeMatlabString(nameList[idx].toASCIIString()).c_str());
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
mxDestroyArray(dimensionSizes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMatlabHelper::getFeatureVectorOutputHeader(const size_t index, CMatrix* matrix) const
|
||||
{
|
||||
const std::string cmd = std::string("[OV_ERRNO, OV_NB_FEATURES, OV_LABELS] = OV_getFeatureVectorOutputHeader(")
|
||||
+ m_boxInstanceVariableName.toASCIIString() + "," + std::to_string(index + 1) + ");";
|
||||
OV_ERROR_UNLESS_KRF(engEvalString(m_matlabEngine, cmd.c_str()) == 0 && getUi32FromEnv("OV_ERRNO") == 0,
|
||||
"Could not get Feature Vector output header",
|
||||
Kernel::ErrorType::BadProcessing);
|
||||
|
||||
const size_t nbFeatures = getUi32FromEnv("OV_NB_FEATURES");
|
||||
std::vector<CString> nameList = getNamelist("OV_LABELS");
|
||||
// Check nb features == nb names ?
|
||||
|
||||
matrix->resize(nbFeatures);
|
||||
for (size_t x = 0; x < nbFeatures; ++x) { matrix->setDimensionLabel(0, x, escapeMatlabString(nameList[x].toASCIIString()).c_str()); }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMatlabHelper::getSignalOutputHeader(const size_t index, CMatrix* matrix, uint64_t& frequency) const
|
||||
{
|
||||
const std::string cmd = std::string("[OV_ERRNO, OV_NB_CHANNELS, OV_NB_SAMPLES_PER_BUFFER, OV_CHANNEL_NAMES, OV_SAMPLING_RATE] = OV_getSignalOutputHeader(")
|
||||
+ m_boxInstanceVariableName.toASCIIString() + "," + std::to_string(index + 1) + ");";
|
||||
OV_ERROR_UNLESS_KRF(engEvalString(m_matlabEngine, cmd.c_str()) == 0 && getUi32FromEnv("OV_ERRNO") == 0, "Could not get Signal output header",
|
||||
Kernel::ErrorType::BadProcessing);
|
||||
|
||||
const size_t nbChannels = getUi32FromEnv("OV_NB_CHANNELS");
|
||||
const size_t nbSamples = getUi32FromEnv("OV_NB_SAMPLES_PER_BUFFER");
|
||||
std::vector<CString> nameList = getNamelist("OV_CHANNEL_NAMES");
|
||||
const size_t rate = getUi32FromEnv("OV_SAMPLING_RATE");
|
||||
|
||||
if (nameList.size() != nbChannels) { return false; }
|
||||
|
||||
matrix->resize(nbChannels, nbSamples);
|
||||
frequency = rate;
|
||||
|
||||
for (size_t x = 0; x < nbChannels; ++x) { matrix->setDimensionLabel(0, x, escapeMatlabString(nameList[x].toASCIIString()).c_str()); }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMatlabHelper::getChannelLocalisationOutputHeader(const size_t index, CMatrix* matrix, bool& /*dynamic*/) const
|
||||
{
|
||||
const std::string cmd = std::string("[OV_ERRNO, OV_NB_CHANNELS, OV_CHANNEL_NAMES, OV_DYNAMIC] = OV_getChannelLocalisationOutputHeader(")
|
||||
+ m_boxInstanceVariableName.toASCIIString() + "," + std::to_string(index + 1) + ");";
|
||||
OV_ERROR_UNLESS_KRF(engEvalString(m_matlabEngine, cmd.c_str()) == 0 && getUi32FromEnv("OV_ERRNO") == 0,
|
||||
"Could not get Channel Localisation output header", Kernel::ErrorType::BadProcessing);
|
||||
|
||||
const size_t nChannel = getUi32FromEnv("OV_NB_CHANNELS");
|
||||
std::vector<CString> nameList = getNamelist("OV_CHANNEL_NAMES");
|
||||
mxArray* dynamic = engGetVariable(m_matlabEngine, "OV_DYNAMIC");
|
||||
if (!dynamic)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Nonexisting variable [OV_DYNAMIC]\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nameList.size() != nChannel) { return false; }
|
||||
|
||||
matrix->resize(nChannel, 3);
|
||||
|
||||
for (size_t x = 0; x < nChannel; ++x) { matrix->setDimensionLabel(0, x, escapeMatlabString(nameList[x].toASCIIString()).c_str()); }
|
||||
|
||||
mxDestroyArray(dynamic);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMatlabHelper::getSpectrumOutputHeader(const size_t index, CMatrix* matrix, CMatrix* frequencyAbscissa, uint64_t& frequency) const
|
||||
{
|
||||
const std::string command = std::string(
|
||||
"[OV_ERRNO, OV_NB_CHANNELS, OV_CHANNEL_NAMES, OV_NB_BANDS, OV_BANDS_NAME, OV_BANDS_LINEAR, OV_SAMPLING_RATE] = OV_getSpectrumOutputHeader(")
|
||||
+ m_boxInstanceVariableName.toASCIIString() + "," + std::to_string(index + 1) + ");";
|
||||
OV_ERROR_UNLESS_KRF(engEvalString(m_matlabEngine, command.c_str()) == 0 && getUi32FromEnv("OV_ERRNO") == 0,
|
||||
"Could not get Spectrum output header", Kernel::ErrorType::BadProcessing);
|
||||
|
||||
const size_t nChannel = getUi32FromEnv("OV_NB_CHANNELS");
|
||||
std::vector<CString> nameList = getNamelist("OV_CHANNEL_NAMES");
|
||||
const size_t nAbscissa = getUi32FromEnv("OV_NB_ABSCISSAS");
|
||||
std::vector<CString> freqAbscissaNames = getNamelist("OV_ABSCISSAS_NAME");
|
||||
mxArray* freqAbscissa = engGetVariable(m_matlabEngine, "OV_ABSCISSAS_LINEAR");
|
||||
if (!freqAbscissa)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Nonexisting variable [OV_ABSCISSAS_LINEAR]\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
frequency = getUi64FromEnv("OV_SAMPLING_RATE");
|
||||
|
||||
//The Frequency abscissa list has dimensions nb_bands
|
||||
frequencyAbscissa->resize(nAbscissa);
|
||||
for (size_t x = 0; x < nAbscissa; ++x) { frequencyAbscissa->setDimensionLabel(0, x, escapeMatlabString(freqAbscissaNames[x].toASCIIString()).c_str()); }
|
||||
|
||||
// Adding the bands:
|
||||
memcpy(frequencyAbscissa->getBuffer(), ::mxGetPr(freqAbscissa), nAbscissa * sizeof(double));
|
||||
|
||||
matrix->resize(nChannel, nAbscissa);
|
||||
for (size_t x = 0; x < nChannel; ++x) { matrix->setDimensionLabel(0, x, escapeMatlabString(nameList[x].toASCIIString()).c_str()); }
|
||||
for (size_t x = 0; x < nAbscissa; ++x) { matrix->setDimensionLabel(1, x, escapeMatlabString(freqAbscissaNames[x].toASCIIString()).c_str()); }
|
||||
// @FIXME CERT is it me or it never copy data to matrix ?
|
||||
|
||||
mxDestroyArray(freqAbscissa);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMatlabHelper::getStimulationsOutputHeader(size_t /*index*/, IStimulationSet* /*stimulationSet*/)
|
||||
{
|
||||
// Nothing to do, the stimulation header is empty.
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
bool CMatlabHelper::popStreamedMatrixOutputBuffer(const size_t index, CMatrix* matrix, uint64_t& startTime, uint64_t& endTime) const
|
||||
{
|
||||
const std::string buf = std::string("[") + m_boxInstanceVariableName.toASCIIString()
|
||||
+ ", OV_START_TIME, OV_END_TIME, OV_LINEAR_DATA_SIZE, OV_LINEAR_DATA] = OV_popOutputBufferReshape("
|
||||
+ m_boxInstanceVariableName.toASCIIString() + ", " + std::to_string(index + 1) + ");";
|
||||
const size_t res = engEvalString(m_matlabEngine, buf.c_str());
|
||||
if (res != 0) { return false; }
|
||||
|
||||
startTime = genUi64FromEnvConverted("OV_START_TIME");
|
||||
endTime = genUi64FromEnvConverted("OV_END_TIME");
|
||||
const uint64_t size = getUi64FromEnv("OV_LINEAR_DATA_SIZE");
|
||||
mxArray* data = engGetVariable(m_matlabEngine, "OV_LINEAR_DATA");
|
||||
if (!data)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Nonexisting variable [OV_LINEAR_DATA]\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// ti be copied direclty in openvibe buffer, the linear matrix must be ordered line by line
|
||||
memcpy(matrix->getBuffer(), ::mxGetPr(data), size * sizeof(double));
|
||||
mxDestroyArray(data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMatlabHelper::popStimulationsOutputBuffer(const size_t index, IStimulationSet* stimSet, uint64_t& startTime, uint64_t& endTime) const
|
||||
{
|
||||
const std::string buf = std::string("[") + m_boxInstanceVariableName.toASCIIString()
|
||||
+ ", OV_START_TIME, OV_END_TIME, OV_LINEAR_MATRIX_SIZE, OV_LINEAR_DATA] = OV_popOutputBuffer("
|
||||
+ m_boxInstanceVariableName.toASCIIString() + ", " + std::to_string(index + 1) + ");";
|
||||
const size_t res = engEvalString(m_matlabEngine, buf.c_str());
|
||||
if (res != 0) { return false; }
|
||||
startTime = genUi64FromEnvConverted("OV_START_TIME");
|
||||
endTime = genUi64FromEnvConverted("OV_END_TIME");
|
||||
const size_t size = getUi32FromEnv("OV_LINEAR_MATRIX_SIZE");
|
||||
mxArray* data = engGetVariable(m_matlabEngine, "OV_LINEAR_DATA");
|
||||
if (!data)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Nonexisting variable [OV_LINEAR_DATA]\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < size; i += 3)
|
||||
{
|
||||
const uint64_t id = castFromMArray(data, i + 0);
|
||||
const uint64_t date = convertFromMArray(data, i + 1);
|
||||
const uint64_t duration = convertFromMArray(data, i + 2);
|
||||
stimSet->appendStimulation(id, date, duration);
|
||||
}
|
||||
|
||||
mxDestroyArray(data);
|
||||
|
||||
return true;
|
||||
}
|
||||
//--------------------------------------------------------------------------------------------------------------
|
||||
|
||||
} // namespace Matlab
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyMatlab
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyMatlab
|
||||
|
||||
#include "ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <engine.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Matlab {
|
||||
class CMatlabHelper
|
||||
{
|
||||
public:
|
||||
CMatlabHelper(Kernel::ILogManager& /*logManager*/, Kernel::IErrorManager& /*errorManager*/) { }
|
||||
|
||||
bool setManagers(Kernel::ILogManager* logManager, Kernel::IErrorManager* errorManager)
|
||||
{
|
||||
m_logManager = logManager;
|
||||
m_errorManager = errorManager;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setStreamedMatrixInputHeader(size_t index, CMatrix* matrix) const;
|
||||
bool setFeatureVectorInputHeader(size_t index, CMatrix* matrix) const;
|
||||
bool setSignalInputHeader(size_t index, CMatrix* matrix, uint64_t frequency) const;
|
||||
bool setChannelLocalisationInputHeader(size_t index, CMatrix* matrix, bool dynamic) const;
|
||||
bool setSpectrumInputHeader(size_t index, CMatrix* matrix, CMatrix* frequencyAbscissa, uint64_t frequency) const;
|
||||
bool setStimulationsInputHeader(size_t index) const;
|
||||
|
||||
// The input buffers for streamed matrix and its children streams are the same.
|
||||
bool addStreamedMatrixInputBuffer(size_t index, CMatrix* matrix, uint64_t startTime, uint64_t endTime) const;
|
||||
bool addStimulationsInputBuffer(size_t index, IStimulationSet* stimSet, uint64_t startTime, uint64_t endTime) const;
|
||||
|
||||
bool getStreamedMatrixOutputHeader(size_t index, CMatrix* matrix) const;
|
||||
bool getFeatureVectorOutputHeader(size_t index, CMatrix* matrix) const;
|
||||
bool getSignalOutputHeader(size_t index, CMatrix* matrix, uint64_t& frequency) const;
|
||||
bool getChannelLocalisationOutputHeader(size_t index, CMatrix* matrix, bool& dynamic) const;
|
||||
bool getSpectrumOutputHeader(size_t index, CMatrix* matrix, CMatrix* frequencyAbscissa, uint64_t& frequency) const;
|
||||
static bool getStimulationsOutputHeader(size_t index, IStimulationSet* stimSet);
|
||||
|
||||
// The output buffers for streamed matrix and its children streams are the same.
|
||||
bool popStreamedMatrixOutputBuffer(size_t index, CMatrix* matrix, uint64_t& startTime, uint64_t& endTime) const;
|
||||
bool popStimulationsOutputBuffer(size_t index, IStimulationSet* stimSet, uint64_t& startTime, uint64_t& endTime) const;
|
||||
|
||||
void setMatlabEngine(Engine* engine) { m_matlabEngine = engine; }
|
||||
void setBoxInstanceVariableName(const CString& name) { m_boxInstanceVariableName = name; }
|
||||
|
||||
|
||||
uint32_t getUi32FromEnv(const char* name) const;
|
||||
uint64_t getUi64FromEnv(const char* name) const;
|
||||
uint64_t genUi64FromEnvConverted(const char* name) const;
|
||||
std::vector<CString> getNamelist(const char* name) const;
|
||||
|
||||
|
||||
Kernel::ILogManager& getLogManager() const { return *m_logManager; }
|
||||
Kernel::IErrorManager& getErrorManager() const { return *m_errorManager; }
|
||||
|
||||
private:
|
||||
Engine* m_matlabEngine = nullptr;
|
||||
|
||||
// Need to be very careful that these pointers are still valid when the Helper will try to use them
|
||||
Kernel::ILogManager* m_logManager = nullptr;
|
||||
Kernel::IErrorManager* m_errorManager = nullptr;
|
||||
|
||||
CString m_boxInstanceVariableName; //must be unique
|
||||
};
|
||||
} // namespace Matlab
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyMatlab
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
// Boxes
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#define OVP_ClassId_BoxAlgorithm_MatlabScripting OpenViBE::CIdentifier(0x03303E0E, 0x39FE10DF)
|
||||
#define OVP_ClassId_BoxAlgorithm_MatlabScriptingDesc OpenViBE::CIdentifier(0x46130E2F, 0x34F90BA1)
|
||||
|
||||
// Global defines
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#ifdef TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
#include "ovp_global_defines.h"
|
||||
#endif // TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
|
||||
#define OV_AttributeId_Box_FlagIsUnstable OpenViBE::CIdentifier(0x666FFFFF, 0x666FFFFF)
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "ovp_defines.h"
|
||||
|
||||
#include "box-algorithms/ovpCBoxAlgorithmMatlabScripting.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Matlab {
|
||||
|
||||
OVP_Declare_Begin()
|
||||
#if defined TARGET_HAS_ThirdPartyMatlab
|
||||
|
||||
OVP_Declare_New(CBoxAlgorithmMatlabScriptingDesc);
|
||||
context.getTypeManager().registerEnumerationEntry(OV_TypeId_BoxAlgorithmFlag, OV_AttributeId_Box_FlagIsUnstable.toString(),
|
||||
OV_AttributeId_Box_FlagIsUnstable.id());
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyMatlab
|
||||
|
||||
OVP_Declare_End()
|
||||
|
||||
} // namespace Matlab
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
Reference in New Issue
Block a user