Commit 408b909b authored by nfoy's avatar nfoy
Browse files

Merge remote-tracking branch 'origin/wip-jlindgre-dll-bridge' into integration-1.2

parents bac04899 07573013
PROJECT(openvibe-plugins-dll-bridge)
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
SET(PROJECT_VERSION_MINOR ${OV_GLOBAL_VERSION_MINOR})
SET(PROJECT_VERSION_PATCH ${OV_GLOBAL_VERSION_PATCH})
SET(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
FILE(GLOB_RECURSE source_files src/*.cpp src/*.h src/*.inl)
ADD_LIBRARY(${PROJECT_NAME} SHARED ${source_files})
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
COMPILE_FLAGS "-DOV_Exports -DOV_Shared")
INCLUDE("FindOpenViBE")
INCLUDE("FindOpenViBECommon")
INCLUDE("FindOpenViBEToolkit")
INCLUDE("FindOpenViBEModuleEBML")
INCLUDE("FindOpenViBEModuleSystem")
# ---------------------------------
# Target macros
# Defines target operating system
# Defines target architecture
# Defines target compiler
# ---------------------------------
IF(WIN32)
ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE)
ADD_DEFINITIONS(-DTARGET_OS_Windows)
ADD_DEFINITIONS(-DTARGET_ARCHITECTURE_i386)
ADD_DEFINITIONS(-DTARGET_COMPILER_VisualStudio)
ENDIF(WIN32)
IF(UNIX)
# ADD_DEFINITIONS(-fvisibility=hidden) # This flag should be present... man gcc
ADD_DEFINITIONS(-g)
ADD_DEFINITIONS(-fnon-call-exceptions)
ADD_DEFINITIONS(-DTARGET_OS_Linux)
ADD_DEFINITIONS(-DTARGET_ARCHITECTURE_i386)
ADD_DEFINITIONS(-DTARGET_COMPILER_GCC)
ENDIF(UNIX)
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR})
#INSTALL(DIRECTORY src/ DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR} FILES_MATCHING PATTERN "*.h" PATTERN ".svn" EXCLUDE PATTERN "doc" EXCLUDE)
Example how to use a Fortran component to do signal processing
On Windows,
- Compile the Fortran code with a 32(!) bit fortran compiler.
The Intel Fortran compiler has been observed to work. e.g.
> where ifort
C:\Program Files (x86)\Intel\Composer XE 2013 SP1\bin\ia32\ifort
> ifort /static /dll fortranbox_dll.f90
The resulting .dll file can be specified to the DLL Bridge box.
Remember to set the full path correctly.
Using the /static flag allows you to distribute your built dll
to other computers more easily.
On Linux,
# gfortran -shared fortranbox_dll.f90 -o fortranbox_dll.so
Then specify the .so file to the DLL Bridge box.
!
! This code illustrates passing a matrix in from OpenViBE, making modifications,
! and sending another matrix back. Care must be taken that the difference in C/Fortran
! row/column major order of the matrix storage is taken into account.
!
! This example adds the channel number to the channel data (so each channel will have noticable and different effect).
! It also adds one channel with a constant pattern, to illustrate how to change the channel count. The
! change of dimensions must be specified in the box_process_header() function.
!
Module box
! Matrix dimensions in and out
INTEGER g_rowsIn,g_colsIn, g_rowsOut,g_colsOut
! Sampling rates in and out
INTEGER g_samplingRateIn, g_samplingRateOut
CONTAINS
! this routine is run once at the beginning of playback. it can be used to precompute/load things needed later.
! set errorCode to 0 on success, a non-zero code otherwise.
subroutine box_init(inputParamsLength, inputParams, errorCode) bind(C)
!DEC$ ATTRIBUTES DLLEXPORT :: box_init
use,intrinsic:: iso_c_binding
implicit none
INTEGER(C_INT32_T), intent(in) :: inputParamsLength;
CHARACTER(KIND=C_CHAR), dimension(inputParamsLength), intent(in) :: inputParams
INTEGER(C_INT32_T), intent(out) :: errorCode;
! in this example, the routine does nothing much
write(*,*) "box_init() in Fortran"
write(*,*) " Received params: ", inputParams
errorCode = 0
end subroutine box_init
! this routine is run when OpenViBE receives a stream header. The purpose of this
! function is to return the sizes of the output matrices, and change in the
! sampling rate if any. If sampling rate is 0, the input of process()
! will be just a matrix without a known sampling frequency.
!
! set errorCode to 0 on success, a non-zero code otherwise.
subroutine box_process_header(rowsIn,colsIn,samplingRateIn,rowsOut,colsOut,samplingRateOut,errorCode) bind(C)
!DEC$ ATTRIBUTES DLLEXPORT :: box_process_header
use iso_c_binding
INTEGER(C_INT32_T), intent(in) :: rowsIn, colsIn, samplingRateIn
INTEGER(C_INT32_T), intent(out) :: rowsOut, colsOut, samplingRateOut
INTEGER(C_INT32_T), intent(out) :: errorCode;
g_rowsIn = rowsIn
g_colsIn = colsIn
g_samplingRateIn = samplingRateIn
! Here rowOut=rowIn+1 since we want to add a channel in this example
g_rowsOut = rowsIn + 1
g_colsOut = colsIn
g_samplingRateOut = samplingRateIn
write(*,*) " Process header: Dims", g_rowsIn, g_colsIn, "->", g_rowsOut, g_colsOut
write(*,*) " Freq", g_SamplingRateIn, "->", g_samplingRateOut
rowsOut = g_rowsOut
colsOut = g_colsOut
samplingRateOut = g_samplingRateOut
errorCode = 0;
end subroutine box_process_header
! this routine is runs once for each matrix chunk of data.
! set errorCode to 0 on success, a non-zero code otherwise.
subroutine box_process(matIn,matOut,errorCode) bind(C)
!DEC$ ATTRIBUTES DLLEXPORT :: box_process
use iso_c_binding
REAL(C_DOUBLE), intent(in) :: matIn(g_rowsIn*g_colsIn)
REAL(C_DOUBLE), intent(out) :: matOut(g_rowsOut*g_colsOut)
INTEGER(C_INT32_T), intent(out) :: errorCode
REAL, DIMENSION(1:g_rowsIn,1:g_colsIn) :: A;
REAL, DIMENSION(1:g_rowsOut,1:g_colsOut) :: B;
! write(*,*) "box_process() in Fortran" , rows, " ", cols
! matrix from C/C++ is in row-major order, reshape
A = reshape(matIn, (/ g_rowsIn, g_colsIn /), ORDER = (/ 2, 1 /) )
! Add +nChannel to each channel
do i=1,g_rowsIn
do j=1,g_colsIn
B(i,j) = A(i,j) + i;
end do
end do
! the new channel just has a simple pattern
do j=1,g_colsOut
B(g_rowsOut,j) = j;
end do
! print the matrix
!WRITE(*,*)
!DO I = LBOUND(B,1), UBOUND(B,1)
! WRITE(*,*) (B(I,J), J = LBOUND(B,2), UBOUND(B,2))
!END DO
matOut = reshape( TRANSPOSE(B), (/ g_rowsOut * g_colsOut /) )
errorCode = 0
end subroutine box_process
! this routine is run once at the end of playback. it can be used to deallocate resources (memory, files, ...).
! set errorCode to 0 on success, a non-zero code otherwise.
subroutine box_uninit(errorCode) bind(C)
!DEC$ ATTRIBUTES DLLEXPORT :: box_uninit
use iso_c_binding
INTEGER(C_INT32_T), intent(out) :: errorCode
! in this example, the routine does nothing much
write(*,*) "box_uninit() in Fortran"
errorCode = 0
end subroutine box_uninit
end module box
#include "ovpCBoxAlgorithmDLLBridge.h"
#include <system/ovCMemory.h>
#include <iostream>
#include <cstdio>
#if defined(TARGET_OS_Windows)
#include <windows.h>
#elif defined(TARGET_OS_Linux)
#include <dlfcn.h>
#endif
using namespace OpenViBE;
using namespace OpenViBE::Plugins;
using namespace OpenViBE::Kernel;
using namespace OpenViBEPlugins;
using namespace OpenViBEPlugins::DLLBridge;
using namespace OpenViBEToolkit;
using namespace std;
// #define DLLBRIDGE_DEBUG
#if defined(DLLBRIDGE_DEBUG)
static void dumpMatrix(OpenViBE::Kernel::ILogManager &rMgr, const CMatrix& mat, const CString &desc)
{
rMgr << LogLevel_Info << desc << "\n";
for(uint32 i=0;i<mat.getDimensionSize(0);i++) {
rMgr << LogLevel_Info << "Row " << i << ": ";
for(uint32 j=0;j<mat.getDimensionSize(1);j++) {
rMgr << mat.getBuffer()[i*mat.getDimensionSize(1)+j] << " ";
}
rMgr << "\n";
}
}
#endif
namespace OpenViBEPlugins
{
namespace DLLBridge
{
// This function is called once when user presses play in Designer
OpenViBE::boolean CDLLBridge::initialize()
{
this->getLogManager() << LogLevel_Debug << "Initializing\n";
m_pEncoder = NULL;
m_pDecoder = NULL;
m_pLibrary = NULL;
m_pInitialize = NULL;
m_pProcessHeader = NULL;
m_pProcess = NULL;
m_pUninitialize = NULL;
m_sDLLFile = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
m_sParameters = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
if(m_sDLLFile == CString(""))
{
this->getLogManager() << LogLevel_Error << "Need a DLL file\n";
return false;
}
#if defined(TARGET_OS_Windows)
std::string tmp = m_sDLLFile.toASCIIString();
std::replace( tmp.begin(), tmp.end(), '/', '\\');
m_sDLLFile = tmp.c_str();
m_pLibrary = LoadLibrary(m_sDLLFile.toASCIIString());
#elif defined(TARGET_OS_Linux)
m_pLibrary = dlopen(m_sDLLFile.toASCIIString(), RTLD_LAZY);
#endif
if(!m_pLibrary)
{
#if defined(TARGET_OS_Windows)
this->getLogManager() << LogLevel_Error << "Failed to load " << m_sDLLFile << ", error code " << (int64) GetLastError() << "\n";
#elif defined(TARGET_OS_Linux)
this->getLogManager() << LogLevel_Error << "Failed to load " << m_sDLLFile << ", error code " << (int64) dlerror() << "\n";
#endif
return false;
}
#if defined(TARGET_OS_Windows)
m_pInitialize = (INITFUNC)GetProcAddress(m_pLibrary, "box_init");
#elif defined(TARGET_OS_Linux)
m_pInitialize = (INITFUNC)dlsym(m_pLibrary, "box_init");
#endif
if(!m_pInitialize)
{
this->getLogManager() << LogLevel_Error << "Unable to find box_init() in the DLL\n";
return false;
}
#if defined(TARGET_OS_Windows)
m_pProcess = (PROCESSFUNC)GetProcAddress(m_pLibrary, "box_process");
#elif defined(TARGET_OS_Linux)
m_pProcess = (PROCESSFUNC)dlsym(m_pLibrary, "box_process");
#endif
if(!m_pProcess)
{
this->getLogManager() << LogLevel_Error << "Unable to find box_process() in the DLL\n";
return false;
}
#if defined(TARGET_OS_Windows)
m_pProcessHeader = (PROCESSHEADERFUNC)GetProcAddress(m_pLibrary, "box_process_header");
#elif defined(TARGET_OS_Linux)
m_pProcessHeader = (PROCESSHEADERFUNC)dlsym(m_pLibrary, "box_process_header");
#endif
if(!m_pProcessHeader)
{
this->getLogManager() << LogLevel_Error << "Unable to find box_process_header() in the DLL\n";
return false;
}
#if defined(TARGET_OS_Windows)
m_pUninitialize = (UNINITFUNC)GetProcAddress(m_pLibrary, "box_uninit");
#elif defined(TARGET_OS_Linux)
m_pUninitialize = (UNINITFUNC)dlsym(m_pLibrary, "box_uninit");
#endif
if(!m_pUninitialize)
{
this->getLogManager() << LogLevel_Error << "Unable to find box_uninit() in the DLL\n";
return false;
}
IBox& l_rStaticBoxContext=this->getStaticBoxContext();
l_rStaticBoxContext.getInputType(0, m_oInputType);
if(m_oInputType == OV_TypeId_StreamedMatrix) {
OpenViBEToolkit::TStreamedMatrixDecoder< CDLLBridge >* l_pDecoder
= new OpenViBEToolkit::TStreamedMatrixDecoder< CDLLBridge >;
OpenViBEToolkit::TStreamedMatrixEncoder< CDLLBridge >* l_pEncoder
= new OpenViBEToolkit::TStreamedMatrixEncoder< CDLLBridge >;
l_pDecoder->initialize(*this, 0);
l_pEncoder->initialize(*this, 0);
m_pDecoder = l_pDecoder;
m_pEncoder = l_pEncoder;
}
else if(m_oInputType == OV_TypeId_Signal)
{
OpenViBEToolkit::TSignalDecoder< CDLLBridge >* l_pDecoder
= new OpenViBEToolkit::TSignalDecoder < CDLLBridge >;
OpenViBEToolkit::TSignalEncoder< CDLLBridge >* l_pEncoder
= new OpenViBEToolkit::TSignalEncoder< CDLLBridge >;
l_pDecoder->initialize(*this, 0);
l_pEncoder->initialize(*this, 0);
m_pDecoder = l_pDecoder;
m_pEncoder = l_pEncoder;
}
else
{
this->getLogManager() << LogLevel_Error << "Unknown input type " << m_oInputType << ". This should never happen.\n";
return false;
}
this->getLogManager() << LogLevel_Trace << "DLL box_init() : Calling\n";
// Do some initialization in DLL
int32 l_i32ParamsLength = m_sParameters.length();
int32 l_i32ErrorCode = 0;
m_pInitialize(&l_i32ParamsLength, m_sParameters.toASCIIString(), &l_i32ErrorCode);
if(l_i32ErrorCode)
{
this->getLogManager() << LogLevel_Error << "DLL box_init() : Returned error code " << l_i32ErrorCode << "\n";
return false;
}
else
{
this->getLogManager() << LogLevel_Trace << "DLL box_init() : Return ok\n";
}
return true;
}
// This function is called once when user presses Stop in Designer
OpenViBE::boolean CDLLBridge::uninitialize()
{
this->getLogManager() << LogLevel_Debug << "Uninitializing\n";
if(m_pUninitialize)
{
this->getLogManager() << LogLevel_Trace << "DLL box_uninit() : Calling\n";
// Do some uninitialization in DLL
int32 l_i32ErrorCode = 0;
m_pUninitialize(&l_i32ErrorCode);
if(l_i32ErrorCode)
{
this->getLogManager() << LogLevel_Error << "DLL box_uninit() : Returned error code " << l_i32ErrorCode << "\n";
return false;
}
else
{
this->getLogManager() << LogLevel_Trace << "DLL box_uninit() : Return ok\n";
}
}
if(m_pEncoder)
{
m_pEncoder->uninitialize();
delete m_pEncoder;
m_pEncoder = NULL;
}
if(m_pDecoder)
{
m_pDecoder->uninitialize();
delete m_pDecoder;
m_pDecoder = NULL;
}
if(m_pLibrary)
{
#if defined(TARGET_OS_Windows)
FreeLibrary(m_pLibrary);
#elif defined(TARGET_OS_Linux)
dlclose(m_pLibrary);
#endif
m_pLibrary = NULL;
}
return true;
}
OpenViBE::boolean CDLLBridge::processInput(uint32 ui32InputIndex)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
// This function is called for every signal chunk (matrix of N samples [channels x samples]).
OpenViBE::boolean CDLLBridge::process()
{
this->getLogManager() << LogLevel_Debug << "Process chunk\n";
IBoxIO& l_rDynamicBoxContext=this->getDynamicBoxContext();
for(uint32 i=0; i<l_rDynamicBoxContext.getInputChunkCount(0); i++)
{
// m_pDecoder/m_pEncoder should point to StreamedMatrix*coder or Signal*Coder by construction,
// the latter appears to be static castable to the former, in practice.
// n.b. dynamic cast does not work here runtime (this does, for the moment). I do not hazard
// an explanation here. Anybody wanting to find out the reasons be prepared to digest the
// codec template inheritance relationships in toolkit for a while.
OpenViBEToolkit::TStreamedMatrixDecoder< CDLLBridge >* l_pDecoder
= static_cast< OpenViBEToolkit::TStreamedMatrixDecoder< CDLLBridge >* >(m_pDecoder);
OpenViBEToolkit::TStreamedMatrixEncoder< CDLLBridge >* l_pEncoder
= static_cast< OpenViBEToolkit::TStreamedMatrixEncoder< CDLLBridge >* >(m_pEncoder);
l_pDecoder->decode(i);
if(l_pDecoder->isHeaderReceived())
{
if(l_pDecoder->getOutputMatrix()->getDimensionCount()!=2)
{
this->getLogManager() << LogLevel_Error << "Only 2 dimensional matrices supported\n";
return false;
}
int32 l_i32SamplingRateIn = 0;
if(m_oInputType == OV_TypeId_Signal)
{
OpenViBEToolkit::TSignalDecoder< CDLLBridge >* l_pSignalDecoder
= static_cast< OpenViBEToolkit::TSignalDecoder< CDLLBridge >* >(m_pDecoder);
l_i32SamplingRateIn = static_cast<int32>(l_pSignalDecoder->getOutputSamplingRate());
}
int32 l_i32nRowsIn = l_pDecoder->getOutputMatrix()->getDimensionSize(0);
int32 l_i32nColsIn = l_pDecoder->getOutputMatrix()->getDimensionSize(1);
this->getLogManager() << LogLevel_Trace << "DLL box_process_header() : Calling\n";
int32 l_i32ErrorCode = 0;
int32 l_i32nRowsOut=0,l_i32nColsOut=0;
int32 l_i32SamplingRateOut = 0;
m_pProcessHeader(&l_i32nRowsIn, &l_i32nColsIn, &l_i32SamplingRateIn, &l_i32nRowsOut, &l_i32nColsOut, &l_i32SamplingRateOut, &l_i32ErrorCode);
if(l_i32ErrorCode)
{
this->getLogManager() << LogLevel_Error << "DLL box_process_header() : Returned error code " << l_i32ErrorCode << "\n";
return false;
}
else
{
this->getLogManager() << LogLevel_Trace << "DLL box_process_header() : Return ok\n";
this->getLogManager() << LogLevel_Trace << "The function declared"
<< " rowsOut=" << l_i32nRowsOut
<< " colsOut=" << l_i32nColsOut
<< " sRateOut=" << l_i32SamplingRateOut
<< "\n";
}
l_pEncoder->getInputMatrix()->setDimensionCount(2);
l_pEncoder->getInputMatrix()->setDimensionSize(0, l_i32nRowsOut);
l_pEncoder->getInputMatrix()->setDimensionSize(1, l_i32nColsOut);
if(m_oInputType == OV_TypeId_Signal)
{
OpenViBEToolkit::TSignalEncoder< CDLLBridge >* l_pSignalEncoder
= static_cast< OpenViBEToolkit::TSignalEncoder< CDLLBridge >* >(m_pEncoder);
l_pSignalEncoder->getInputSamplingRate() = l_i32SamplingRateOut;
}
l_pEncoder->encodeHeader();
l_rDynamicBoxContext.markOutputAsReadyToSend(0, l_rDynamicBoxContext.getInputChunkStartTime(0, i), l_rDynamicBoxContext.getInputChunkEndTime(0, i));
}
if(l_pDecoder->isBufferReceived())
{
float64* l_pInput = l_pDecoder->getOutputMatrix()->getBuffer();
float64* l_pOutput = l_pEncoder->getInputMatrix()->getBuffer();
this->getLogManager() << LogLevel_Trace << "DLL box_process() : Calling\n";
// Process the sample chunk in DLL
int32 l_i32ErrorCode = 0;
m_pProcess(l_pInput, l_pOutput, &l_i32ErrorCode);
if(l_i32ErrorCode)
{
this->getLogManager() << LogLevel_Error << "DLL box_process() : Returned error code " << l_i32ErrorCode << "\n";
return false;
}
else
{
this->getLogManager() << LogLevel_Trace << "DLL box_process() : Return ok\n";
}
l_pEncoder->encodeBuffer();
l_rDynamicBoxContext.markOutputAsReadyToSend(0, l_rDynamicBoxContext.getInputChunkStartTime(0, i), l_rDynamicBoxContext.getInputChunkEndTime(0, i));
}
if(l_pDecoder->isEndReceived())
{
l_pEncoder->encodeEnd();
l_rDynamicBoxContext.markOutputAsReadyToSend(0, l_rDynamicBoxContext.getInputChunkStartTime(0, i), l_rDynamicBoxContext.getInputChunkEndTime(0, i));
}
}
return true;
}
};
};
#ifndef __OpenViBEPlugins_CDLLBridge_H__
#define __OpenViBEPlugins_CDLLBridge_H__
#include "../ovp_defines.h"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <string>
#include <vector>
#include <queue>
#include <cstdio>