init
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
|
||||
# Add all the subdirs as projects of the named branch
|
||||
OV_ADD_PROJECTS("CONTRIB_PLUGINS_PROCESSING")
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
|
||||
# fill this once there is a contribution to this category...
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
// Global defines //
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
#ifdef TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
#include "ovp_global_defines.h"
|
||||
#endif // TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
|
||||
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
|
||||
#include <vector>
|
||||
#include <openvibe/ov_all.h>
|
||||
#include "ovp_defines.h"
|
||||
|
||||
OVP_Declare_Begin();
|
||||
|
||||
// For the moment this plugin is empty
|
||||
|
||||
OVP_Declare_End()
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
PROJECT(openvibe-plugins-contrib-file-io)
|
||||
|
||||
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
|
||||
SET(PROJECT_VERSION ${OV_GLOBAL_VERSION_STRING})
|
||||
|
||||
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.inl src/*.c)
|
||||
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
|
||||
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
FOLDER ${PLUGINS_FOLDER}
|
||||
COMPILE_FLAGS "-DOVP_Exports -DOVP_Shared -D_LARGEFILE64_SOURCE -D_LARGEFILE_SOURCE")
|
||||
|
||||
# ---------------------------------
|
||||
INCLUDE("FindOpenViBE")
|
||||
INCLUDE("FindOpenViBECommon")
|
||||
INCLUDE("FindOpenViBEToolkit")
|
||||
INCLUDE("FindOpenViBEModuleEBML")
|
||||
INCLUDE("FindThirdPartyBoost")
|
||||
|
||||
# -----------------------------
|
||||
# Install files
|
||||
# -----------------------------
|
||||
INSTALL(TARGETS ${PROJECT_NAME}
|
||||
RUNTIME DESTINATION ${DIST_BINDIR}
|
||||
LIBRARY DESTINATION ${DIST_LIBDIR}
|
||||
ARCHIVE DESTINATION ${DIST_LIBDIR})
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* \page BoxAlgorithm_EDFFileWriter EDF File Writer
|
||||
__________________________________________________________________
|
||||
|
||||
Detailed description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_EDFFileWriter_Description|
|
||||
|
||||
* Writes EEG data in European Data Format (EDF).
|
||||
|
||||
* |OVP_DocEnd_BoxAlgorithm_EDFFileWriter_Description|
|
||||
__________________________________________________________________
|
||||
|
||||
Inputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_EDFFileWriter_Inputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_EDFFileWriter_Inputs|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_EDFFileWriter_Input1|
|
||||
* Experiment Information stream, coming from an acquisition client/server.
|
||||
* |OVP_DocEnd_BoxAlgorithm_EDFFileWriter_Input1|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_EDFFileWriter_Input2|
|
||||
* EEG signal.
|
||||
* |OVP_DocEnd_BoxAlgorithm_EDFFileWriter_Input2|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_EDFFileWriter_Input3|
|
||||
* Stimulation stream.
|
||||
* |OVP_DocEnd_BoxAlgorithm_EDFFileWriter_Input3|
|
||||
|
||||
Settings description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_EDFFileWriter_Settings|
|
||||
* |OVP_DocEnd_BoxAlgorithm_EDFFileWriter_Settings|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_EDFFileWriter_Setting1|
|
||||
* The file name to be written.
|
||||
* |OVP_DocEnd_BoxAlgorithm_EDFFileWriter_Setting1|
|
||||
|
||||
__________________________________________________________________
|
||||
|
||||
Examples description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_EDFFileWriter_Examples|
|
||||
* |OVP_DocEnd_BoxAlgorithm_EDFFileWriter_Examples|
|
||||
__________________________________________________________________
|
||||
|
||||
Miscellaneous description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_EDFFileWriter_Miscellaneous|
|
||||
EDF format can be read by several programs, (e.g EDF browser).
|
||||
This box is based on the free software EDFlib by Teunis van Beelen.
|
||||
* |OVP_DocEnd_BoxAlgorithm_EDFFileWriter_Miscellaneous|
|
||||
*/
|
||||
+4419
File diff suppressed because it is too large
Load Diff
+535
@@ -0,0 +1,535 @@
|
||||
/*
|
||||
*****************************************************************************
|
||||
*
|
||||
* Copyright (c) 2009, 2010, 2011 Teunis van Beelen
|
||||
* All rights reserved.
|
||||
*
|
||||
* email: teuniz@gmail.com
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY Teunis van Beelen ''AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL Teunis van Beelen BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*****************************************************************************
|
||||
*/
|
||||
|
||||
|
||||
/* compile with options "-D_LARGEFILE64_SOURCE -D_LARGEFILE_SOURCE" */
|
||||
|
||||
|
||||
#ifndef EDFLIB_INCLUDED
|
||||
#define EDFLIB_INCLUDED
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#define EDFLIB_TIME_DIMENSION (10000000LL)
|
||||
#define EDFLIB_MAXSIGNALS 256
|
||||
#define EDFLIB_MAX_ANNOTATION_LEN 512
|
||||
|
||||
#define EDFSEEK_SET 0
|
||||
#define EDFSEEK_CUR 1
|
||||
#define EDFSEEK_END 2
|
||||
|
||||
|
||||
/* the following defines are used in the member "filetype" of the edf_hdr_struct */
|
||||
/* and as return value for the function edfopen_file_readonly() */
|
||||
#define EDFLIB_FILETYPE_EDF 0
|
||||
#define EDFLIB_FILETYPE_EDFPLUS 1
|
||||
#define EDFLIB_FILETYPE_BDF 2
|
||||
#define EDFLIB_FILETYPE_BDFPLUS 3
|
||||
#define EDFLIB_MALLOC_ERROR -1
|
||||
#define EDFLIB_NO_SUCH_FILE_OR_DIRECTORY -2
|
||||
#define EDFLIB_FILE_CONTAINS_FORMAT_ERRORS -3
|
||||
#define EDFLIB_MAXFILES_REACHED -4
|
||||
#define EDFLIB_FILE_READ_ERROR -5
|
||||
#define EDFLIB_FILE_ALREADY_OPENED -6
|
||||
#define EDFLIB_FILETYPE_ERROR -7
|
||||
#define EDFLIB_FILE_WRITE_ERROR -8
|
||||
#define EDFLIB_NUMBER_OF_SIGNALS_INVALID -9
|
||||
#define EDFLIB_FILE_IS_DISCONTINUOUS -10
|
||||
#define EDFLIB_INVALID_READ_ANNOTS_VALUE -11
|
||||
|
||||
/* values for annotations */
|
||||
#define EDFLIB_DO_NOT_READ_ANNOTATIONS 0
|
||||
#define EDFLIB_READ_ANNOTATIONS 1
|
||||
#define EDFLIB_READ_ALL_ANNOTATIONS 2
|
||||
|
||||
/* the following defines are possible errors returned by edfopen_file_writeonly() */
|
||||
#define EDFLIB_NO_SIGNALS -20
|
||||
#define EDFLIB_TOO_MANY_SIGNALS -21
|
||||
#define EDFLIB_NO_SAMPLES_IN_RECORD -22
|
||||
#define EDFLIB_DIGMIN_IS_DIGMAX -23
|
||||
#define EDFLIB_DIGMAX_LOWER_THAN_DIGMIN -24
|
||||
#define EDFLIB_PHYSMIN_IS_PHYSMAX -25
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
struct edf_param_struct // this structure contains all the relevant EDF-signal parameters of one signal
|
||||
{
|
||||
char label[17]; // label (name) of the signal, null-terminated string
|
||||
long long smp_in_file; // number of samples of this signal in the file
|
||||
double phys_max; // physical maximum
|
||||
double phys_min; // physical minimum
|
||||
int dig_max; // digital maximum
|
||||
int dig_min; // digital minimum
|
||||
int smp_in_datarecord; // number of samples of this signal in a datarecord
|
||||
char physdimension[9]; // physical dimension (uV, bpm, mA, etc.), null-terminated string
|
||||
char prefilter[81]; // null-terminated string
|
||||
char transducer[81]; // null-terminated string
|
||||
};
|
||||
|
||||
|
||||
struct edf_annotation_struct // this structure is used for annotations
|
||||
{
|
||||
long long onset; // onset time of the event, expressed in units of 100 nanoSeconds
|
||||
char duration[16]; // duration time, this is a null-terminated ASCII text-string
|
||||
char annotation[EDFLIB_MAX_ANNOTATION_LEN + 1]; // description of the event in UTF-8, this is a null terminated string
|
||||
};
|
||||
|
||||
|
||||
struct edf_hdr_struct // this structure contains all the relevant EDF header info and will be filled when calling the function edf_open_file_readonly()
|
||||
{
|
||||
int handle; // a handle (identifier) used to distinguish the different files
|
||||
int filetype; // 0: EDF, 1: EDFplus, 2: BDF, 3: BDFplus, a negative number means an error
|
||||
int edfsignals; // number of EDF signals in the file, annotation channels are NOT included
|
||||
long long file_duration; // duration of the file expressed in units of 100 nanoSeconds
|
||||
int startdate_day;
|
||||
int startdate_month;
|
||||
int startdate_year;
|
||||
long long
|
||||
starttime_subsecond; // starttime offset expressed in units of 100 nanoSeconds. Is always less than 10000000 (one second). Only used by EDFplus and BDFplus
|
||||
int starttime_second;
|
||||
int starttime_minute;
|
||||
int starttime_hour;
|
||||
char patient[81]; // null-terminated string, contains patientfield of header, is always empty when filetype is EDFPLUS or BDFPLUS
|
||||
char recording[81
|
||||
]; // null-terminated string, contains recordingfield of header, is always empty when filetype is EDFPLUS or BDFPLUS
|
||||
char patientcode[81]; // null-terminated string, is always empty when filetype is EDF or BDF
|
||||
char gender[16]; // null-terminated string, is always empty when filetype is EDF or BDF
|
||||
char birthdate[16]; // null-terminated string, is always empty when filetype is EDF or BDF
|
||||
char patient_name[81]; // null-terminated string, is always empty when filetype is EDF or BDF
|
||||
char patient_additional[81]; // null-terminated string, is always empty when filetype is EDF or BDF
|
||||
char admincode[81]; // null-terminated string, is always empty when filetype is EDF or BDF
|
||||
char technician[81]; // null-terminated string, is always empty when filetype is EDF or BDF
|
||||
char equipment[81]; // null-terminated string, is always empty when filetype is EDF or BDF
|
||||
char recording_additional[81]; // null-terminated string, is always empty when filetype is EDF or BDF
|
||||
long long datarecord_duration; // duration of a datarecord expressed in units of 100 nanoSeconds
|
||||
long long datarecords_in_file; // number of datarecords in the file
|
||||
long long annotations_in_file; // number of annotations in the file
|
||||
struct edf_param_struct signalparam[EDFLIB_MAXSIGNALS]; // array of structs which contain the relevant signal parameters
|
||||
};
|
||||
|
||||
|
||||
int edflib_version();
|
||||
|
||||
/* Returns the version number of this library, multiplied by hundred. if version is "1.00" than it will return 100 */
|
||||
|
||||
|
||||
/***************** the following functions are used to read files **************************/
|
||||
|
||||
|
||||
int EdfopenFileReadonly(const char* path, struct edf_hdr_struct* edfhdr, const int readAnnotations);
|
||||
|
||||
/* opens an existing file for reading */
|
||||
/* path is a null-terminated string containing the path to the file */
|
||||
/* hdr is a pointer to an edf_hdr_struct, all fields in this struct will be overwritten */
|
||||
/* the edf_hdr_struct will be filled with all the relevant header- and signalinfo/parameters */
|
||||
|
||||
/* read_annotations must have one of the following values: */
|
||||
/* EDFLIB_DO_NOT_READ_ANNOTATIONS annotations will not be read (this saves time when opening a very large EDFplus or BDFplus file */
|
||||
/* EDFLIB_READ_ANNOTATIONS annotations will be read immediately, stops when an annotation has */
|
||||
/* been found which contains the description "Recording ends" */
|
||||
/* EDFLIB_READ_ALL_ANNOTATIONS all annotations will be read immediately */
|
||||
|
||||
/* returns 0 on success, in case of an error it returns -1 and an errorcode will be set in the member "filetype" of struct edf_hdr_struct */
|
||||
/* This function is required if you want to read a file */
|
||||
|
||||
|
||||
int EdfcloseFile(const int handle);
|
||||
|
||||
/* closes and finalizes the file */
|
||||
/* returns -1 in case of an error, 0 on success */
|
||||
/* this function MUST be called when you are finished reading or writing */
|
||||
/* This function is required after reading or writing. Failing to do so will cause */
|
||||
/* unnessecary memory usage and in case of writing it will cause a corrupted and incomplete file */
|
||||
|
||||
|
||||
int EdfreadPhysicalSamples(const int handle, const int edfsignal, int n, double* buf);
|
||||
|
||||
/* reads n samples from edfsignal, starting from the current sample position indicator, into buf (edfsignal starts at 0) */
|
||||
/* the values are converted to their physical values e.g. microVolts, beats per minute, etc. */
|
||||
/* bufsize should be equal to or bigger than sizeof(double[n]) */
|
||||
/* the sample position indicator will be increased with the amount of samples read */
|
||||
/* returns the amount of samples read (this can be less than n or zero!) */
|
||||
/* or -1 in case of an error */
|
||||
|
||||
|
||||
int EdfreadDigitalSamples(const int handle, const int edfsignal, int n, int* buf);
|
||||
|
||||
/* reads n samples from edfsignal, starting from the current sample position indicator, into buf (edfsignal starts at 0) */
|
||||
/* the values are the "raw" digital values */
|
||||
/* bufsize should be equal to or bigger than sizeof(int[n]) */
|
||||
/* the sample position indicator will be increased with the amount of samples read */
|
||||
/* returns the amount of samples read (this can be less than n or zero!) */
|
||||
/* or -1 in case of an error */
|
||||
|
||||
|
||||
long long edfseek(const int handle, const int edfsignal, const long long offset, const int whence);
|
||||
|
||||
/* The edfseek() function sets the sample position indicator for the edfsignal pointed to by edfsignal. */
|
||||
/* The new position, measured in samples, is obtained by adding offset samples to the position specified by whence. */
|
||||
/* If whence is set to EDFSEEK_SET, EDFSEEK_CUR, or EDFSEEK_END, the offset is relative to the start of the file, */
|
||||
/* the current position indicator, or end-of-file, respectively. */
|
||||
/* Returns the current offset. Otherwise, -1 is returned. */
|
||||
/* note that every signal has it's own independent sample position indicator and edfseek() affects only one of them */
|
||||
|
||||
|
||||
long long edftell(const int handle, const int edfsignal);
|
||||
|
||||
/* The edftell() function obtains the current value of the sample position indicator for the edfsignal pointed to by edfsignal. */
|
||||
/* Returns the current offset. Otherwise, -1 is returned */
|
||||
/* note that every signal has it's own independent sample position indicator and edftell() affects only one of them */
|
||||
|
||||
|
||||
void edfrewind(const int handle, const int edfsignal);
|
||||
|
||||
/* The edfrewind() function sets the sample position indicator for the edfsignal pointed to by edfsignal to the beginning of the file. */
|
||||
/* It is equivalent to: () edfseek(int handle, int edfsignal, 0LL, EDFSEEK_SET) */
|
||||
/* note that every signal has it's own independent sample position indicator and edfrewind() affects only one of them */
|
||||
|
||||
|
||||
int EdfGetAnnotation(const int handle, const int n, struct edf_annotation_struct* annot);
|
||||
|
||||
/* Fills the edf_annotation_struct with the annotation n, returns 0 on success, otherwise -1 */
|
||||
/* To obtain the number of annotations in a file, check edf_hdr_struct -> annotations_in_file */
|
||||
|
||||
/*
|
||||
Notes:
|
||||
|
||||
Annotationsignals
|
||||
|
||||
EDFplus and BDFplus store the annotations in one or more signals (in order to be backwards compatibel with EDF and BDF).
|
||||
The counting of the signals in the file starts at 0. Signals used for annotations are skipped by EDFlib.
|
||||
This means that the annotationsignal(s) in the file are hided.
|
||||
Use the function edf_get_annotation() to get the annotations.
|
||||
|
||||
So, when a file contains 5 signals and the third signal is used to store the annotations, the library will
|
||||
report that there are only 4 signals in the file.
|
||||
The library will "map" the signalnumbers as follows: 0->0, 1->1, 2->3, 3->4.
|
||||
This way you don't need to worry about which signals are annotationsignals. The library will do it for you.
|
||||
|
||||
How the library stores time-values
|
||||
|
||||
To avoid rounding errors, the library stores some timevalues in variables of type long long int.
|
||||
In order not to loose the subsecond precision, all timevalues have been multiplied by 10000000.
|
||||
This will limit the timeresolution to 100 nanoSeconds. To calculate the amount of seconds, divide
|
||||
the timevalue by 10000000 or use the macro EDFLIB_TIME_DIMENSION which is declared in edflib.h.
|
||||
The following variables do use this when you open a file in read mode: "file_duration", "starttime_subsecond" and "onset".
|
||||
*/
|
||||
|
||||
|
||||
/***************** the following functions are used to write files **************************/
|
||||
|
||||
|
||||
int EdfopenFileWriteonly(const char* path, const int filetype, const int nSignals);
|
||||
|
||||
/* opens an new file for writing. warning, an already existing file with the same name will be silently overwritten without advance warning!! */
|
||||
/* path is a null-terminated string containing the path and name of the file */
|
||||
/* filetype must be EDFLIB_FILETYPE_EDFPLUS or EDFLIB_FILETYPE_BDFPLUS */
|
||||
/* returns a handle on success, you need this handle for the other functions */
|
||||
/* in case of an error it returns a negative number corresponding to one of the following values: */
|
||||
/* EDFLIB_MALLOC_ERROR */
|
||||
/* EDFLIB_NO_SUCH_FILE_OR_DIRECTORY */
|
||||
/* EDFLIB_MAXFILES_REACHED */
|
||||
/* EDFLIB_FILE_ALREADY_OPENED */
|
||||
/* EDFLIB_NUMBER_OF_SIGNALS_INVALID */
|
||||
/* This function is required if you want to write a file */
|
||||
|
||||
|
||||
int EdfSetSampling(const int handle, const int edfsignal, const int sampling);
|
||||
|
||||
/* Sets the sampling of signal edfsignal. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is required for every signal and can be called only after opening a */
|
||||
/* file in writemode and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetPhysicalMaximum(const int handle, const int edfsignal, const double max);
|
||||
|
||||
/* Sets the maximum physical value of signal edfsignal. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is required for every signal and can be called only after opening a */
|
||||
/* file in writemode and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetPhysicalMinimum(const int handle, const int edfsignal, const double min);
|
||||
|
||||
/* Sets the minimum physical value of signal edfsignal. */
|
||||
/* Usually this will be (-(phys_max)) */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is required for every signal and can be called only after opening a */
|
||||
/* file in writemode and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetDigitalMaximum(const int handle, const int edfsignal, const int max);
|
||||
|
||||
/* Sets the maximum digital value of signal edfsignal. Usually, the value 32767 is used for EDF+ and 8388607 for BDF+ */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is required for every signal and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetDigitalMinimum(const int handle, const int edfsignal, const int min);
|
||||
|
||||
/* Sets the minimum digital value of signal edfsignal. Usually, the value -32768 is used for EDF+ and -8388608 for BDF+ */
|
||||
/* Usually this will be (-(dig_max + 1)) */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is required for every signal and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetLabel(const int handle, const int edfsignal, const char* label);
|
||||
|
||||
/* Sets the label (name) of signal edfsignal. ("FP1", "SaO2", etc.) */
|
||||
/* label is a pointer to a NULL-terminated ASCII-string containing the label (name) of the signal edfsignal */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is recommended for every signal when you want to write a file */
|
||||
/* and can be called only after opening a file in writemode and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetPrefilter(const int handle, const int edfsignal, const char* prefilter);
|
||||
|
||||
/* Sets the prefilter of signal edfsignal ("HP:0.1Hz", "LP:75Hz N:50Hz", etc.). */
|
||||
/* prefilter is a pointer to a NULL-terminated ASCII-string containing the prefilter text of the signal edfsignal */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode and before */
|
||||
/* the first sample write action */
|
||||
|
||||
|
||||
int EdfSetTransducer(const int handle, const int edfsignal, const char* transducer);
|
||||
|
||||
/* Sets the transducer of signal edfsignal ("AgAgCl cup electrodes", etc.). */
|
||||
/* transducer is a pointer to a NULL-terminated ASCII-string containing the transducer text of the signal edfsignal */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode and before */
|
||||
/* the first sample write action */
|
||||
|
||||
|
||||
int EdfSetPhysicalDimension(const int handle, const int edfsignal, const char* dim);
|
||||
|
||||
/* Sets the physical dimension of signal edfsignal. ("uV", "BPM", "mA", "Degr.", etc.) */
|
||||
/* phys_dim is a pointer to a NULL-terminated ASCII-string containing the physical dimension of the signal edfsignal */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is recommended for every signal when you want to write a file */
|
||||
/* and can be called only after opening a file in writemode and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetStartdatetime(const int handle, const int year, const int month, const int day, const int hour, const int minute, const int second);
|
||||
|
||||
/* Sets the startdate and starttime. */
|
||||
/* year: 1970 - 3000, month: 1 - 12, day: 1 - 31 */
|
||||
/* hour: 0 - 23, minute: 0 - 59, second: 0 - 59 */
|
||||
/* If not called, the library will use the system date and time at runtime */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetPatientname(const int handle, const char* patientname);
|
||||
|
||||
/* Sets the patientname. patientname is a pointer to a null-terminated ASCII-string. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetPatientcode(const int handle, const char* patientcode);
|
||||
|
||||
/* Sets the patientcode. patientcode is a pointer to a null-terminated ASCII-string. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetGender(const int handle, const int gender);
|
||||
|
||||
/* Sets the gender. 1 is male, 0 is female. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetBirthdate(const int handle, const int year, const int month, const int day);
|
||||
|
||||
/* Sets the birthdate. */
|
||||
/* year: 1800 - 3000, month: 1 - 12, day: 1 - 31 */
|
||||
/* This function is optional */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetPatientAdditional(const int handle, const char* additional);
|
||||
|
||||
/* Sets the additional patientinfo. patient_additional is a pointer to a null-terminated ASCII-string. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetAdmincode(const int handle, const char* admincode);
|
||||
|
||||
/* Sets the admincode. admincode is a pointer to a null-terminated ASCII-string. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetTechnician(const int handle, const char* technician);
|
||||
|
||||
/* Sets the technicians name. technician is a pointer to a null-terminated ASCII-string. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetEquipment(const int handle, const char* equipment);
|
||||
|
||||
/* Sets the name of the equipment used during the aquisition. equipment is a pointer to a null-terminated ASCII-string. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfSetRecordingAdditional(const int handle, const char* additional);
|
||||
|
||||
/* Sets the additional recordinginfo. recording_additional is a pointer to a null-terminated ASCII-string. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before the first sample write action */
|
||||
|
||||
|
||||
int EdfwritePhysicalSamples(const int handle, const double* buf);
|
||||
|
||||
/* Writes n physical samples (uV, mA, Ohm) from *buf belonging to one signal */
|
||||
/* where n is the sampling of the signal. */
|
||||
/* The physical samples will be converted to digital samples using the */
|
||||
/* values of physical maximum, physical minimum, digital maximum and digital minimum */
|
||||
/* The number of samples written is equal to the sampling of the signal */
|
||||
/* Size of buf should be equal to or bigger than sizeof(double[sampling]) */
|
||||
/* Call this function for every signal in the file. The order is important! */
|
||||
/* When there are 4 signals in the file, the order of calling this function */
|
||||
/* must be: signal 0, signal 1, signal 2, signal 3, signal 0, signal 1, signal 2, etc. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
|
||||
|
||||
int EdfBlockwritePhysicalSamples(const int handle, const double* buf);
|
||||
|
||||
/* Writes physical samples (uV, mA, Ohm) from *buf */
|
||||
/* buf must be filled with samples from all signals, starting with n samples of signal 0, n samples of signal 1, n samples of signal 2, etc. */
|
||||
/* where n is the sampling of the signal. */
|
||||
/* buf must be filled with samples from all signals, starting with signal 0, 1, 2, etc. */
|
||||
/* one block equals one second */
|
||||
/* The physical samples will be converted to digital samples using the */
|
||||
/* values of physical maximum, physical minimum, digital maximum and digital minimum */
|
||||
/* The number of samples written is equal to the sum of the samplefrequencies of all signals */
|
||||
/* Size of buf should be equal to or bigger than sizeof(double) multiplied by the sum of the samplefrequencies of all signals */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
|
||||
|
||||
int EdfwriteDigitalSamples(const int handle, const int* buf);
|
||||
|
||||
/* Writes n "raw" digital samples from *buf belonging to one signal */
|
||||
/* where n is the sampling of the signal. */
|
||||
/* The 16 (or 24 in case of BDF) least significant bits of the sample will be written to the */
|
||||
/* file without any conversion. */
|
||||
/* The number of samples written is equal to the sampling of the signal */
|
||||
/* Size of buf should be equal to or bigger than sizeof(int[sampling]) */
|
||||
/* Call this function for every signal in the file. The order is important! */
|
||||
/* When there are 4 signals in the file, the order of calling this function */
|
||||
/* must be: signal 0, signal 1, signal 2, signal 3, signal 0, signal 1, signal 2, etc. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
|
||||
|
||||
int EdfBlockwriteDigitalSamples(const int handle, const int* buf);
|
||||
|
||||
/* Writes "raw" digital samples from *buf. */
|
||||
/* buf must be filled with samples from all signals, starting with n samples of signal 0, n samples of signal 1, n samples of signal 2, etc. */
|
||||
/* where n is the sampling of the signal. */
|
||||
/* One block equals one second. */
|
||||
/* The 16 (or 24 in case of BDF) least significant bits of the sample will be written to the */
|
||||
/* file without any conversion. */
|
||||
/* The number of samples written is equal to the sum of the samplefrequencies of all signals. */
|
||||
/* Size of buf should be equal to or bigger than sizeof(int) multiplied by the sum of the samplefrequencies of all signals */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
|
||||
|
||||
int EdfwriteAnnotationUTF8(const int handle, const long long onset, const long long duration, const char* description);
|
||||
|
||||
/* writes an annotation/event to the file */
|
||||
/* onset is relative to the starttime and startdate of the file */
|
||||
/* onset and duration are in units of 100 microSeconds! resolution is 0.0001 second! */
|
||||
/* for example: 34.071 seconds must be written as 340710 */
|
||||
/* if duration is unknown or not applicable: set a negative number (-1) */
|
||||
/* description is a null-terminated UTF8-string containing the text that describes the event */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before closing the file */
|
||||
|
||||
|
||||
int EdfwriteAnnotationLatin1(const int handle, const long long onset, const long long duration, const char* description);
|
||||
|
||||
/* writes an annotation/event to the file */
|
||||
/* onset is relative to the starttime and startdate of the file */
|
||||
/* onset and duration are in units of 100 microSeconds! resolution is 0.0001 second! */
|
||||
/* for example: 34.071 seconds must be written as 340710 */
|
||||
/* if duration is unknown or not applicable: set a negative number (-1) */
|
||||
/* description is a null-terminated Latin1-string containing the text that describes the event */
|
||||
/* This function is optional and can be called only after opening a file in writemode */
|
||||
/* and before closing the file */
|
||||
|
||||
|
||||
int EdfSetDatarecordDuration(const int handle, const int duration);
|
||||
|
||||
/* Sets the datarecord duration. The default value is 1 second. */
|
||||
/* ATTENTION: the argument "duration" is expressed in units of 10 microSeconds! */
|
||||
/* So, if you want to set the datarecord duration to 0.1 second, you must give */
|
||||
/* the argument "duration" a value of "10000". */
|
||||
/* This function is optional, normally you don't need to change the default value. */
|
||||
/* The datarecord duration must be in the range 0.025 to 20.0 seconds. */
|
||||
/* Returns 0 on success, otherwise -1 */
|
||||
/* This function is NOT REQUIRED but can be called after opening a */
|
||||
/* file in writemode and before the first sample write action. */
|
||||
/* This function can be used when you want to use a samplerate */
|
||||
/* which is not an integer. For example, if you want to use a samplerate of 0.5 Hz, */
|
||||
/* set the sampling to 5 Hz and the datarecord duration to 10 seconds, */
|
||||
/* or set the sampling to 1 Hz and the datarecord duration to 2 seconds. */
|
||||
/* Do not use this function, except when absolutely necessary! */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+315
@@ -0,0 +1,315 @@
|
||||
/* Project: Gipsa-lab plugins for OpenVibe
|
||||
* AUTHORS AND CONTRIBUTORS: Andreev A., Barachant A., Congedo M., Ionescu,Gelu,
|
||||
|
||||
* This file is part of "Gipsa-lab plugins for OpenVibe".
|
||||
* You can redistribute it and/or modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
* This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License along with Brain Invaders. If not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
#include "ovpCBoxAlgorithmBrainampFileWriterGipsa.h"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace FileIO {
|
||||
|
||||
|
||||
//documentation Appendix B EEG file format: http://tsgdoc.socsci.ru.nl/images/d/d1/BrainVision_Recorder_UM.pdf
|
||||
|
||||
bool CBoxAlgorithmBrainampFileWriterGipsa::initialize()
|
||||
{
|
||||
m_isVmrkHeaderFileWritten = false;
|
||||
|
||||
//init input signal 1
|
||||
m_streamDecoder = new Toolkit::TSignalDecoder<CBoxAlgorithmBrainampFileWriterGipsa>(*this, 0);
|
||||
|
||||
//init input stimulation 1
|
||||
m_stimDecoder = new Toolkit::TStimulationDecoder<CBoxAlgorithmBrainampFileWriterGipsa>(*this, 1);
|
||||
|
||||
//Get parameters:
|
||||
const CString filename = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
|
||||
const CString tmp = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
|
||||
const CIdentifier binaryFormatID = this->getTypeManager().getEnumerationEntryValueFromName(OVP_TypeId_BinaryFormat, tmp);
|
||||
|
||||
if (binaryFormatID == OVP_TypeId_BinaryFormat_int16_t) { m_binaryFormat = BinaryFormat_Integer16; }
|
||||
else if (binaryFormatID == OVP_TypeId_BinaryFormat_uint16_t) { m_binaryFormat = BinaryFormat_UnsignedInteger16; }
|
||||
else if (binaryFormatID == OVP_TypeId_BinaryFormat_float) { m_binaryFormat = BinaryFormat_Float32; }
|
||||
else
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Unknown binary format: " << binaryFormatID << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
//Perform checks:
|
||||
if (std::string(filename).empty())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Header file path is empty!\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::string(filename).substr(filename.length() - 5, 5) != std::string(".vhdr"))
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Warning << "The supplied output file does not end with .vhdr\n";
|
||||
}
|
||||
|
||||
m_headerFilename = filename;
|
||||
|
||||
m_headerFile.open(m_headerFilename.c_str());
|
||||
if (!m_headerFile.good())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Could not open header file [" << m_headerFilename << "]\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
//this->getLogManager() << Kernel::LogLevel_ImportantWarning << m_sHeaderFilename << "\n";
|
||||
|
||||
m_dataFilename = m_headerFilename.substr(0, m_headerFilename.length() - 5) + std::string(".eeg");
|
||||
|
||||
m_dataFile.open(m_dataFilename.c_str(), std::ios::binary);
|
||||
if (!m_dataFile.good())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Could not open data file [" << m_dataFilename << "]\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_markerFilename = m_headerFilename.substr(0, m_headerFilename.length() - 5) + std::string(".vmrk");
|
||||
|
||||
m_markerFile.open(m_markerFilename.c_str());
|
||||
if (!m_markerFile.good())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Could not open marker file [" << m_markerFilename << "]\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmBrainampFileWriterGipsa::uninitialize()
|
||||
{
|
||||
if (m_streamDecoder)
|
||||
{
|
||||
m_streamDecoder->uninitialize();
|
||||
delete m_streamDecoder;
|
||||
}
|
||||
|
||||
// uninit input stimulation
|
||||
if (m_stimDecoder)
|
||||
{
|
||||
m_stimDecoder->uninitialize();
|
||||
delete m_stimDecoder;
|
||||
}
|
||||
|
||||
//close files
|
||||
m_headerFile.flush();
|
||||
m_headerFile.close();
|
||||
|
||||
m_dataFile.flush();
|
||||
m_dataFile.close();
|
||||
|
||||
m_markerFile.flush();
|
||||
m_markerFile.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmBrainampFileWriterGipsa::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmBrainampFileWriterGipsa::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
//1. Process signal
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i) //first input channel data
|
||||
{
|
||||
m_streamDecoder->decode(i);
|
||||
|
||||
//HEADER
|
||||
if (m_streamDecoder->isHeaderReceived())
|
||||
{
|
||||
m_matrix = m_streamDecoder->getOutputMatrix();
|
||||
m_sampling = m_streamDecoder->getOutputSamplingRate();
|
||||
|
||||
writeHeaderFile();
|
||||
}
|
||||
|
||||
//BUFFER
|
||||
if (m_streamDecoder->isBufferReceived())
|
||||
{
|
||||
// size_t channelCount = m_Matrix->getDimensionSize(0);
|
||||
switch (m_binaryFormat)
|
||||
{
|
||||
case BinaryFormat_Integer16:
|
||||
saveBuffer(int16_t(0));
|
||||
break;
|
||||
|
||||
case BinaryFormat_UnsignedInteger16:
|
||||
saveBuffer(uint16_t(0));
|
||||
break;
|
||||
|
||||
case BinaryFormat_Float32:
|
||||
saveBuffer(float(0));
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
//END
|
||||
if (m_streamDecoder->isEndReceived()) { }
|
||||
|
||||
boxContext.markInputAsDeprecated(0, i);
|
||||
}
|
||||
|
||||
// Make sure the marker file header is written in any case
|
||||
if (!m_isVmrkHeaderFileWritten)
|
||||
{
|
||||
const boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
|
||||
const std::string formated(formatTime(now));
|
||||
|
||||
m_markerFile << "Brain Vision Data Exchange Marker File, Version 1.0" << std::endl << std::endl
|
||||
<< "[Common Infos]" << std::endl
|
||||
<< "Codepage=ANSI" << std::endl
|
||||
<< "DataFile=" << getShortName(m_dataFilename) << std::endl << std::endl
|
||||
<< "[Marker Infos]" << std::endl
|
||||
<< "; Each entry: Mk<Marker number>=<Type>,<Description>,<Position in data points>," << std::endl
|
||||
<< "; <Size in data points>, <Channel number (0 = marker is related to all channels)>" << std::endl
|
||||
<< "; Fields are delimited by commas, some fields might be omitted (empty)." << std::endl
|
||||
<< "; Commas in type or description text are coded as \"\\1\"." << std::endl
|
||||
<< "Mk1=New Segment,,1,1,0," << formated << "000000" << std::endl;
|
||||
|
||||
m_isVmrkHeaderFileWritten = true;
|
||||
}
|
||||
|
||||
//2. Process stimulations - input channel 1
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(1); ++i)
|
||||
{
|
||||
// uint64_t chunkStartTime =boxContext.getInputChunkStartTime(0, i);
|
||||
|
||||
m_stimDecoder->decode(i);
|
||||
|
||||
//buffer
|
||||
if (m_stimDecoder->isBufferReceived())
|
||||
{
|
||||
IStimulationSet* stimSet = m_stimDecoder->getOutputStimulationSet();
|
||||
|
||||
// Loop on stimulations
|
||||
for (size_t j = 0; j < stimSet->getStimulationCount(); ++j)
|
||||
{
|
||||
const uint64_t code = stimSet->getStimulationIdentifier(j);
|
||||
const uint64_t position = CTime(stimSet->getStimulationDate(j)).toSampleCount(m_sampling) + 1;
|
||||
|
||||
m_markerFile << "Mk" << m_stimulationCounter << "=Stimulus," << "S" << std::right << std::setw(3) << code << "," << position << ",1,0" << std::
|
||||
endl;
|
||||
|
||||
m_stimulationCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
boxContext.markInputAsDeprecated(1, i);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmBrainampFileWriterGipsa::writeHeaderFile()
|
||||
{
|
||||
const size_t nChannel = m_matrix->getDimensionSize(0);
|
||||
// size_t samplesPerChunk = m_Matrix->getDimensionSize(1);
|
||||
|
||||
const double samplingInterval = 1000000.0 / double(m_sampling);
|
||||
|
||||
CString format("UNKNOWN");
|
||||
switch (m_binaryFormat)
|
||||
{
|
||||
case BinaryFormat_Integer16:
|
||||
format = "INT_16";
|
||||
break;
|
||||
|
||||
case BinaryFormat_UnsignedInteger16:
|
||||
format = "UINT_16";
|
||||
break;
|
||||
|
||||
case BinaryFormat_Float32:
|
||||
format = "IEEE_FLOAT_32";
|
||||
break;
|
||||
default:
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "EEG format unknown!\n";
|
||||
break;
|
||||
}
|
||||
|
||||
m_headerFile << "Brain Vision Data Exchange Header File Version 1.0" << std::endl
|
||||
<< "; Data created by the Vision Recorder" << std::endl << std::endl
|
||||
<< "[Common Infos]" << std::endl
|
||||
<< "Codepage=ANSI" << std::endl
|
||||
<< "DataFile=" << getShortName(m_dataFilename) << std::endl
|
||||
<< "MarkerFile=" << getShortName(m_markerFilename) << std::endl
|
||||
<< "DataFormat=BINARY" << std::endl
|
||||
<< "; Data orientation: MULTIPLEXED=ch1,pt1, ch2,pt1 ..." << std::endl //sample 1, sample 2 ...
|
||||
<< "DataOrientation=MULTIPLEXED" << std::endl
|
||||
<< "NumberOfChannels=" << nChannel << std::endl
|
||||
<< "; Sampling interval in microseconds" << std::endl
|
||||
<< "SamplingInterval=" << std::fixed << std::setprecision(5) << samplingInterval << std::endl
|
||||
<< std::endl
|
||||
<< "[Binary Infos]" << std::endl
|
||||
<< "BinaryFormat=" << format.toASCIIString() << std::endl
|
||||
<< std::endl;
|
||||
|
||||
m_headerFile << "[Channel Infos]" << std::endl
|
||||
<< "; Each entry: Ch<Channel number>=<Name>,<Reference channel name>," << std::endl
|
||||
<< "; <Resolution in \"Unit\">,<Unit>, Future extensions.." << std::endl
|
||||
<< "; Fields are delimited by commas, some fields might be omitted (empty)." << std::endl
|
||||
<< "; Commas in channel names are coded as \"\\1\"." << std::endl;
|
||||
|
||||
for (size_t i = 0; i < nChannel; ++i)
|
||||
{
|
||||
m_headerFile << "Ch" << (i + 1) << "=" << m_matrix->getDimensionLabel(0, i) << ",,1," << std::endl; //resolution = 1
|
||||
}
|
||||
|
||||
m_headerFile << std::endl;
|
||||
|
||||
m_headerFile << "[Comment]" << std::endl << std::endl
|
||||
<< "A m p l i f i e r S e t u p" << std::endl
|
||||
<< "============================" << std::endl
|
||||
<< "Number of channels: " << nChannel << std::endl
|
||||
<< "Sampling Rate [Hz]: " << m_sampling << std::endl
|
||||
<< "Interval [µS]: " << std::fixed << std::setprecision(5) << samplingInterval << std::endl
|
||||
<< std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string CBoxAlgorithmBrainampFileWriterGipsa::getShortName(std::string fullpath)
|
||||
{
|
||||
size_t pos = fullpath.find_last_of('\\');
|
||||
if (pos == std::string::npos) { pos = fullpath.find_last_of('/'); }
|
||||
if (pos != std::string::npos) { return fullpath.substr(pos + 1, fullpath.size() - pos); }
|
||||
return fullpath;
|
||||
}
|
||||
|
||||
std::string CBoxAlgorithmBrainampFileWriterGipsa::formatTime(const boost::posix_time::ptime now)
|
||||
{
|
||||
using namespace boost::posix_time;
|
||||
|
||||
static std::locale loc(std::wcout.getloc(), new wtime_facet(L"%Y%m%d%H%M%S"));
|
||||
|
||||
std::basic_stringstream<wchar_t> wss;
|
||||
wss.imbue(loc);
|
||||
wss << now;
|
||||
std::wstring wstr = wss.str();
|
||||
std::string str(wstr.begin(), wstr.end());
|
||||
|
||||
return str;
|
||||
}
|
||||
} // namespace FileIO
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
/* Project: Gipsa-lab plugins for OpenVibe
|
||||
* AUTHORS AND CONTRIBUTORS: Andreev A., Barachant A., Congedo M., Ionescu,Gelu,
|
||||
|
||||
* This file is part of "Gipsa-lab plugins for OpenVibe".
|
||||
* You can redistribute it and/or modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
* This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License along with Brain Invaders. If not, see http://www.gnu.org/licenses/.*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace FileIO {
|
||||
class CBoxAlgorithmBrainampFileWriterGipsa final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
CBoxAlgorithmBrainampFileWriterGipsa() { }
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
bool writeHeaderFile(); //write the .vhdr file
|
||||
|
||||
static std::string getShortName(std::string fullpath); //of a file
|
||||
static std::string formatTime(boost::posix_time::ptime now);
|
||||
|
||||
|
||||
template <class T>
|
||||
bool saveBuffer(const T /*myDummy*/)
|
||||
{
|
||||
std::vector<T> output(m_matrix->getBufferElementCount());
|
||||
|
||||
if (output.size() != m_matrix->getBufferElementCount()) { return false; }
|
||||
|
||||
const size_t nChannel = m_matrix->getDimensionSize(0);
|
||||
const size_t nSamplesPerChunk = m_matrix->getDimensionSize(1);
|
||||
double* input = m_matrix->getBuffer();
|
||||
|
||||
for (size_t k = 0; k < nChannel; ++k)
|
||||
{
|
||||
for (size_t j = 0; j < nSamplesPerChunk; ++j)
|
||||
{
|
||||
const size_t index = (k * nSamplesPerChunk) + j;
|
||||
output[j * nChannel + k] = T(input[index]);
|
||||
}
|
||||
}
|
||||
|
||||
m_dataFile.write((char*)&output[0], m_matrix->getBufferElementCount() * sizeof(T));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_BrainampFileWriterGipsa)
|
||||
|
||||
protected:
|
||||
|
||||
enum EBinaryFormat
|
||||
{
|
||||
BinaryFormat_Integer16,
|
||||
BinaryFormat_UnsignedInteger16,
|
||||
BinaryFormat_Float32,
|
||||
};
|
||||
|
||||
//input signal 1
|
||||
Toolkit::TSignalDecoder<CBoxAlgorithmBrainampFileWriterGipsa>* m_streamDecoder = nullptr;
|
||||
CMatrix* m_matrix = nullptr;
|
||||
uint64_t m_sampling = 0;
|
||||
|
||||
//input stimulation 1
|
||||
Toolkit::TStimulationDecoder<CBoxAlgorithmBrainampFileWriterGipsa>* m_stimDecoder = nullptr;
|
||||
//Kernel::TParameterHandler <const IMemoryBuffer* > ip_bufferToDecodeTrigger;
|
||||
//Kernel::TParameterHandler < IStimulationSet* > op_pStimulationSetTrigger;
|
||||
|
||||
std::string m_headerFilename;
|
||||
std::string m_dataFilename;
|
||||
std::string m_markerFilename;
|
||||
|
||||
std::ofstream m_headerFile;
|
||||
std::ofstream m_dataFile;
|
||||
std::ofstream m_markerFile;
|
||||
|
||||
size_t m_binaryFormat = 0;
|
||||
size_t m_stimulationCounter = 2; //because the first is outputed manually
|
||||
|
||||
bool m_isVmrkHeaderFileWritten = false; //Is the beginning of the .vmrk (file with stimulations is written)
|
||||
};
|
||||
|
||||
class CBoxAlgorithmBrainampFileWriterGipsaListener final : public Toolkit::TBoxListener<IBoxListener>
|
||||
{
|
||||
public:
|
||||
|
||||
bool onInputTypeChanged(Kernel::IBox& /*box*/, const size_t /*index*/) override { return true; }
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
|
||||
};
|
||||
|
||||
class CBoxAlgorithmBrainampFileWriterGipsaDesc final : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("BrainVision Format file writer (Gipsa)"); }
|
||||
CString getAuthorName() const override { return CString("Anton Andreev"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Gipsa-lab"); }
|
||||
CString getShortDescription() const override { return CString("Writes signal in the Brainamp file format."); }
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return CString(
|
||||
"You must select the location of the output header file .vhdr. The .eeg and .vmrk files will be created with the same name and in the same folder. Integer codes of OpenVibe stimulations are saved in the .vmrk file. OV Stimulation with code 233 will be saved as S233 on the .vmrk file.");
|
||||
}
|
||||
|
||||
CString getCategory() const override { return CString("File reading and writing/BrainVision Format"); }
|
||||
CString getVersion() const override { return CString("1.1"); }
|
||||
CString getStockItemName() const override { return CString("gtk-save"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_BrainampFileWriterGipsa; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmBrainampFileWriterGipsa; }
|
||||
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmBrainampFileWriterGipsaListener; }
|
||||
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Streamed matrix", OV_TypeId_Signal);
|
||||
prototype.addInput("Input stimulation channel", OV_TypeId_Stimulations);
|
||||
|
||||
prototype.addSetting("Header filename", OV_TypeId_Filename, "record-[$core{date}-$core{time}].vhdr");
|
||||
prototype.addSetting("Binary format", OVP_TypeId_BinaryFormat, "INT_16");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_BrainampFileWriterGipsaDesc)
|
||||
};
|
||||
} // namespace FileIO
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+314
@@ -0,0 +1,314 @@
|
||||
#include "ovpCBoxAlgorithmEDFFileWriter.h"
|
||||
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace FileIO {
|
||||
|
||||
/*******************************************************************************/
|
||||
|
||||
bool CBoxAlgorithmEDFFileWriter::initialize()
|
||||
{
|
||||
m_experimentInfoDecoder.initialize(*this, 0);
|
||||
m_signalDecoder.initialize(*this, 1);
|
||||
m_stimDecoder.initialize(*this, 2);
|
||||
|
||||
m_filename = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
|
||||
m_sampling = 0;
|
||||
m_nChannels = 0;
|
||||
|
||||
m_isFileOpened = false;
|
||||
m_fileHandle = -1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*******************************************************************************/
|
||||
|
||||
bool CBoxAlgorithmEDFFileWriter::uninitialize()
|
||||
{
|
||||
if (!m_isFileOpened)
|
||||
{
|
||||
// Fine, we didn't manage to write anything
|
||||
this->getLogManager() << Kernel::LogLevel_Warning << "Exiting without writing a file (open failed or no signal header received).\n";
|
||||
|
||||
// Clear the queue
|
||||
std::queue<double> empty;
|
||||
std::swap(m_buffer, empty);
|
||||
|
||||
m_experimentInfoDecoder.uninitialize();
|
||||
m_signalDecoder.uninitialize();
|
||||
m_stimDecoder.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "Writing the file, this may take a moment.\n";
|
||||
|
||||
for (size_t c = 0; c < m_nChannels; ++c)
|
||||
{
|
||||
if (EdfSetSampling(m_fileHandle, c, m_sampling) == -1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edf_set_sampling failed!\n";
|
||||
return false;
|
||||
}
|
||||
if (EdfSetPhysicalMaximum(m_fileHandle, c, m_channelInfo[c].max) == -1)//1.7*10^308)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edf_set_physical_maximum failed!\n";
|
||||
return false;
|
||||
}
|
||||
if (EdfSetPhysicalMinimum(m_fileHandle, c, m_channelInfo[c].min) == -1)//-1.7*10^308)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edf_set_physical_minimum failed!\n";
|
||||
return false;
|
||||
}
|
||||
if (EdfSetDigitalMaximum(m_fileHandle, c, 32767) == -1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edf_set_digital_maximum failed!\n";
|
||||
return false;
|
||||
}
|
||||
if (EdfSetDigitalMinimum(m_fileHandle, c, -32768) == -1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edf_set_digital_minimum failed!\n";
|
||||
return false;
|
||||
}
|
||||
if (EdfSetLabel(m_fileHandle, c, m_signalDecoder.getOutputMatrix()->getDimensionLabel(0, c)) == -1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edf_set_label failed!\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const size_t n = m_sampling * m_nChannels;
|
||||
std::vector<double> tmpBuffer(n), tmpBufferToWrite(n); // A buffer chunk and Transposed buffer chunk
|
||||
|
||||
// Write out all the complete buffers
|
||||
if (m_buffer.size() >= n)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "((int) buffer.size() >= m_sampling*m_NChannels)\n";
|
||||
while (m_buffer.size() >= n)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "while((int)buffer.size() >= m_sampling*m_NChannels)\n";
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
tmpBuffer[i] = double(m_buffer.front());
|
||||
m_buffer.pop();
|
||||
}
|
||||
|
||||
for (size_t c = 0; c < m_nChannels; ++c)
|
||||
{
|
||||
for (size_t s = 0; s < m_sampling; ++s) { tmpBufferToWrite[c * m_sampling + s] = tmpBuffer[s * m_nChannels + c]; }
|
||||
}
|
||||
|
||||
if (EdfBlockwritePhysicalSamples(m_fileHandle, tmpBufferToWrite.data()) == -1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edf_blockwrite_physical_samples: Could not write samples in file [" << m_filename << "]\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have a partial buffer? If so, write it out padded by zeroes
|
||||
if (!m_buffer.empty())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "if(buffer.size() > 0))\n";
|
||||
for (size_t i = 0; i < n; ++i) { tmpBuffer[i] = 0; }
|
||||
|
||||
for (size_t i = 0; i < m_buffer.size(); ++i)
|
||||
{
|
||||
tmpBuffer[i] = double(m_buffer.front());
|
||||
m_buffer.pop();
|
||||
}
|
||||
|
||||
for (size_t c = 0; c < m_nChannels; ++c)
|
||||
{
|
||||
for (size_t s = 0; s < m_sampling; ++s) { tmpBufferToWrite[c * m_sampling + s] = tmpBuffer[s * m_nChannels + c]; }
|
||||
}
|
||||
|
||||
if (EdfBlockwritePhysicalSamples(m_fileHandle, tmpBufferToWrite.data()) == -1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edf_blockwrite_physical_samples: Could not write samples in file [" << m_filename << "]\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (EdfcloseFile(m_fileHandle) == -1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edfclose_file: Could not close file [" << m_filename << "]\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_experimentInfoDecoder.uninitialize();
|
||||
m_signalDecoder.uninitialize();
|
||||
m_stimDecoder.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*******************************************************************************/
|
||||
|
||||
bool CBoxAlgorithmEDFFileWriter::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*******************************************************************************/
|
||||
|
||||
bool CBoxAlgorithmEDFFileWriter::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
//iterate over all chunk on signal input
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(1); ++i)
|
||||
{
|
||||
m_signalDecoder.decode(i);
|
||||
|
||||
if (m_signalDecoder.isHeaderReceived())
|
||||
{
|
||||
m_sampling = size_t(m_signalDecoder.getOutputSamplingRate());
|
||||
m_nChannels = m_signalDecoder.getOutputMatrix()->getDimensionSize(0);
|
||||
m_nSamplesPerChunk = m_signalDecoder.getOutputMatrix()->getDimensionSize(1);
|
||||
|
||||
m_fileHandle = EdfopenFileWriteonly(m_filename.toASCIIString(), EDFLIB_FILETYPE_EDFPLUS, m_nChannels);
|
||||
if (m_fileHandle < 0)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "Could not open file [" << m_filename << "]\n";
|
||||
switch (m_fileHandle)
|
||||
{
|
||||
case EDFLIB_MALLOC_ERROR:
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "EDFLIB_MALLOC_ERROR: ";
|
||||
break;
|
||||
case EDFLIB_NO_SUCH_FILE_OR_DIRECTORY:
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "EDFLIB_NO_SUCH_FILE_OR_DIRECTORY: ";
|
||||
break;
|
||||
case EDFLIB_MAXFILES_REACHED:
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "EDFLIB_MAXFILES_REACHED: ";
|
||||
break;
|
||||
case EDFLIB_FILE_ALREADY_OPENED:
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "EDFLIB_FILE_ALREADY_OPENED: ";
|
||||
break;
|
||||
case EDFLIB_NUMBER_OF_SIGNALS_INVALID:
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "EDFLIB_NUMBER_OF_SIGNALS_INVALID: ";
|
||||
break;
|
||||
default:
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "EDFLIB_UNKNOWN_ERROR: ";
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//set file parameters
|
||||
EdfSetStartdatetime(m_fileHandle, 0, 0, 0, 0, 0, 0);
|
||||
|
||||
for (size_t c = 0; c < m_nChannels; ++c)
|
||||
{
|
||||
//Creation of one information channel structure per channel
|
||||
channel_info_t channelInfo = { std::numeric_limits<double>::max(), -std::numeric_limits<double>::max() };
|
||||
m_channelInfo.push_back(channelInfo);
|
||||
}
|
||||
|
||||
m_isFileOpened = true;
|
||||
}
|
||||
|
||||
if (m_signalDecoder.isBufferReceived())
|
||||
{
|
||||
//put sample in the buffer
|
||||
CMatrix* matrix = m_signalDecoder.getOutputMatrix();
|
||||
for (size_t s = 0; s < m_nSamplesPerChunk; ++s)
|
||||
{
|
||||
for (size_t c = 0; c < m_nChannels; ++c)
|
||||
{
|
||||
double value = matrix->getBuffer()[c * m_nSamplesPerChunk + s];
|
||||
if (value > m_channelInfo[c].max) { m_channelInfo[c].max = value; }
|
||||
if (value < m_channelInfo[c].min) { m_channelInfo[c].min = value; }
|
||||
m_buffer.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_signalDecoder.isEndReceived()) { }
|
||||
}
|
||||
|
||||
if (m_isFileOpened)
|
||||
{
|
||||
//iterate over all chunk on experiment information input
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
m_experimentInfoDecoder.decode(i);
|
||||
|
||||
if (m_experimentInfoDecoder.isHeaderReceived())
|
||||
{
|
||||
//set patient code
|
||||
EdfSetPatientcode(m_fileHandle, std::to_string(m_experimentInfoDecoder.getOutputSubjectID()).c_str());
|
||||
|
||||
//set patient gender
|
||||
switch (m_experimentInfoDecoder.getOutputSubjectGender())
|
||||
{
|
||||
case 2: //female
|
||||
EdfSetGender(m_fileHandle, 0);
|
||||
break;
|
||||
case 1: //male
|
||||
EdfSetGender(m_fileHandle, 1);
|
||||
break;
|
||||
case 0: //unknown
|
||||
break;
|
||||
case 9: //unspecified
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//set patient age
|
||||
const char* patientAge = ("Patient age = " + std::to_string(m_experimentInfoDecoder.getOutputSubjectAge())).c_str();
|
||||
EdfSetPatientAdditional(m_fileHandle, patientAge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//iterate over all chunk on stimulation input
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(2); ++i)
|
||||
{
|
||||
m_stimDecoder.decode(i);
|
||||
|
||||
if (m_stimDecoder.isHeaderReceived()) { }
|
||||
|
||||
if (m_stimDecoder.isBufferReceived())
|
||||
{
|
||||
const IStimulationSet* stimSet = m_stimDecoder.getOutputStimulationSet();
|
||||
for (size_t j = 0; j < stimSet->getStimulationCount(); ++j)
|
||||
{
|
||||
const uint64_t date = stimSet->getStimulationDate(j);
|
||||
const int64_t stimDate = int64_t(CTime(date).toSeconds() / 0.0001);
|
||||
|
||||
const uint64_t duration = stimSet->getStimulationDuration(j);
|
||||
const int64_t stimDuration = int64_t(CTime(duration).toSeconds() / 0.0001);
|
||||
|
||||
const uint64_t id = stimSet->getStimulationIdentifier(j);
|
||||
CString stimID = this->getTypeManager().getEnumerationEntryNameFromValue(OV_TypeId_Stimulation, id);
|
||||
if (stimID.length() == 0)
|
||||
{
|
||||
// If the stimulation number has not been registered as an enum, just pass the number
|
||||
std::stringstream ss;
|
||||
ss << id;
|
||||
stimID = ss.str().c_str();
|
||||
}
|
||||
const int result = EdfwriteAnnotationUTF8(m_fileHandle, stimDate, stimDuration, stimID.toASCIIString());
|
||||
if (result == -1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "edfwrite_annotation_utf8 failed!\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_stimDecoder.isEndReceived()) { }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace FileIO
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "edf/edflib.h"
|
||||
#include <queue>
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace FileIO {
|
||||
typedef struct
|
||||
{
|
||||
double min;
|
||||
double max;
|
||||
} channel_info_t;
|
||||
|
||||
|
||||
class CBoxAlgorithmEDFFileWriter final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
//CBoxAlgorithmEDFFileWriter();
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_EDFFileWriter)
|
||||
|
||||
protected:
|
||||
|
||||
Toolkit::TExperimentInfoDecoder<CBoxAlgorithmEDFFileWriter> m_experimentInfoDecoder;
|
||||
Toolkit::TSignalDecoder<CBoxAlgorithmEDFFileWriter> m_signalDecoder;
|
||||
Toolkit::TStimulationDecoder<CBoxAlgorithmEDFFileWriter> m_stimDecoder;
|
||||
|
||||
CString m_filename;
|
||||
bool m_isFileOpened = false;
|
||||
int m_fileHandle = 0;
|
||||
size_t m_sampling = 0;
|
||||
size_t m_nChannels = 0;
|
||||
size_t m_nSamplesPerChunk = 0;
|
||||
std::queue<double, std::deque<double>> m_buffer;
|
||||
//int * m_pTemporyBuffer;
|
||||
//int * m_pTemporyBufferToWrite;
|
||||
|
||||
std::vector<channel_info_t> m_channelInfo;
|
||||
};
|
||||
|
||||
class CBoxAlgorithmEDFFileWriterDesc final : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("EDF File Writer"); }
|
||||
CString getAuthorName() const override { return CString("Aurelien Van Langhenhove"); }
|
||||
CString getAuthorCompanyName() const override { return CString("CICIT Garches"); }
|
||||
|
||||
CString getShortDescription() const override { return CString("Writes experiment information, signal and stimulations in a EDF file"); }
|
||||
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString("File reading and writing/EDF"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getStockItemName() const override { return CString("gtk-save"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_EDFFileWriter; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmEDFFileWriter; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Experiment information", OV_TypeId_ExperimentInfo);
|
||||
prototype.addInput("Signal", OV_TypeId_Signal);
|
||||
prototype.addInput("Stimulations", OV_TypeId_Stimulations);
|
||||
|
||||
prototype.addSetting("Filename", OV_TypeId_Filename, "record-[$core{date}-$core{time}].edf");
|
||||
|
||||
//prototype.addFlag(Kernel::BoxFlag_IsUnstable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_EDFFileWriterDesc)
|
||||
};
|
||||
} // namespace FileIO
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
// Boxes
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#define OVP_ClassId_BoxAlgorithm_EDFFileWriter OpenViBE::CIdentifier(0x0D454DCE, 0x470A4C02)
|
||||
#define OVP_ClassId_BoxAlgorithm_EDFFileWriterDesc OpenViBE::CIdentifier(0x0D454DCE, 0x470A4C02)
|
||||
#define OVP_ClassId_BoxAlgorithm_BrainampFileWriterGipsa OpenViBE::CIdentifier(0x0C7E0BDE, 0x4EC90F95)
|
||||
#define OVP_ClassId_BoxAlgorithm_BrainampFileWriterGipsaDesc OpenViBE::CIdentifier(0x0A77142C, 0x316B6E47)
|
||||
|
||||
#define OVP_TypeId_BinaryFormat OpenViBE::CIdentifier(0x567234C5, 0x3D870DC3)
|
||||
#define OVP_TypeId_BinaryFormat_int16_t OpenViBE::CIdentifier(0x1C777556, 0x123861C3)
|
||||
#define OVP_TypeId_BinaryFormat_uint16_t OpenViBE::CIdentifier(0x6A7B6C73, 0x4B9D129D)
|
||||
#define OVP_TypeId_BinaryFormat_float OpenViBE::CIdentifier(0x15183866, 0x7AAC69FC)
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
#include <vector>
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
// @BEGIN CICIT-GARCHES
|
||||
#include "box-algorithms/ovpCBoxAlgorithmEDFFileWriter.h"
|
||||
// @END CICIT-GARCHES
|
||||
|
||||
// @BEGIN GIPSA
|
||||
#include "box-algorithms/ovpCBoxAlgorithmBrainampFileWriterGipsa.h"
|
||||
// @END GIPSA
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace FileIO {
|
||||
|
||||
OVP_Declare_Begin()
|
||||
// @BEGIN CICIT-GARCHES
|
||||
OVP_Declare_New(CBoxAlgorithmEDFFileWriterDesc)
|
||||
// @END CICIT_GARCHES
|
||||
|
||||
// @BEGIN GIPSA
|
||||
//Register dropdowns
|
||||
context.getTypeManager().registerEnumerationType(OVP_TypeId_BinaryFormat, "Binary format select");
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_BinaryFormat, "INT_16", OVP_TypeId_BinaryFormat_int16_t.id());
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_BinaryFormat, "UINT_16", OVP_TypeId_BinaryFormat_uint16_t.id());
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_BinaryFormat, "IEEE_FLOAT_32", OVP_TypeId_BinaryFormat_float.id());
|
||||
|
||||
OVP_Declare_New(CBoxAlgorithmBrainampFileWriterGipsaDesc)
|
||||
// @END GIPSA
|
||||
|
||||
OVP_Declare_End()
|
||||
|
||||
} // namespace Tools
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,28 @@
|
||||
PROJECT(openvibe-plugins-contrib-misc)
|
||||
|
||||
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
|
||||
SET(PROJECT_VERSION ${OV_GLOBAL_VERSION_STRING})
|
||||
|
||||
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.inl)
|
||||
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
|
||||
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
FOLDER ${PLUGINS_FOLDER}
|
||||
COMPILE_FLAGS "-DOVP_Exports -DOVP_Shared")
|
||||
|
||||
# ---------------------------------
|
||||
INCLUDE("FindOpenViBE")
|
||||
INCLUDE("FindOpenViBECommon")
|
||||
INCLUDE("FindOpenViBEToolkit")
|
||||
INCLUDE("FindOpenViBEModuleEBML")
|
||||
# INCLUDE("FindThirdPartyBoost")
|
||||
INCLUDE("FindThirdPartyOpenAL")
|
||||
|
||||
# -----------------------------
|
||||
# Install files
|
||||
# -----------------------------
|
||||
INSTALL(TARGETS ${PROJECT_NAME}
|
||||
RUNTIME DESTINATION ${DIST_BINDIR}
|
||||
LIBRARY DESTINATION ${DIST_LIBDIR}
|
||||
ARCHIVE DESTINATION ${DIST_LIBDIR})
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
#include "ovpCMouseControl.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#if defined TARGET_OS_Linux
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Tools { //Ambiguous without OpenViBEPlugins
|
||||
|
||||
bool CMouseControl::initialize()
|
||||
{
|
||||
m_decoder = new Toolkit::TStreamedMatrixDecoder<CMouseControl>(*this, 0);
|
||||
|
||||
#if !defined(TARGET_OS_Linux)
|
||||
getLogManager() << Kernel::LogLevel_Error << "This box algorithm is not implemented for your operating system\n";
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CMouseControl::uninitialize()
|
||||
{
|
||||
m_decoder->uninitialize();
|
||||
delete m_decoder;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMouseControl::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMouseControl::process()
|
||||
{
|
||||
Kernel::IBoxIO* boxContext = getBoxAlgorithmContext()->getDynamicBoxContext();
|
||||
const size_t nInputChunk = boxContext->getInputChunkCount(0);
|
||||
|
||||
for (size_t i = 0; i < nInputChunk; ++i)
|
||||
{
|
||||
m_decoder->decode(i);
|
||||
if (m_decoder->isBufferReceived())
|
||||
{
|
||||
CMatrix* iMatrix = m_decoder->getOutputMatrix();
|
||||
if (iMatrix->getBufferElementCount() != 1)
|
||||
{
|
||||
getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Error << "Error, dimension size isn't 1 for Amplitude input !\n";
|
||||
return false;
|
||||
}
|
||||
#if defined TARGET_OS_Linux
|
||||
const double* iBuffer = iMatrix->getBuffer();
|
||||
m_pMainDisplay=::XOpenDisplay(NULL);
|
||||
if (!m_pMainDisplay)
|
||||
{
|
||||
getLogManager() << Kernel::LogLevel_Error << "Impossible to open Display.\n";
|
||||
return false;
|
||||
}
|
||||
m_oRootWindow=DefaultRootWindow(m_pMainDisplay); //all X11 screens
|
||||
::XSelectInput(m_pMainDisplay, m_oRootWindow, ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|OwnerGrabButtonMask);
|
||||
|
||||
int offsetY = 0;
|
||||
int offsetX = int(iBuffer[0]*100.0);
|
||||
|
||||
getLogManager() << Kernel::LogLevel_Debug << "offsetX = " << offsetX << "\n";
|
||||
|
||||
::XWarpPointer(m_pMainDisplay, m_oRootWindow, 0, 0, 0, 0, 0, offsetX, offsetY);
|
||||
::XCloseDisplay(m_pMainDisplay);
|
||||
#endif
|
||||
// TODO
|
||||
// For windows use:
|
||||
// SetCursorPos(int x, int y)
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace Tools
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#if defined TARGET_OS_Linux
|
||||
#include <X11/X.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#endif
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Tools {
|
||||
class CMouseControl : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
CMouseControl() { }
|
||||
|
||||
void release() override { delete this; }
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_MouseControl)
|
||||
|
||||
protected:
|
||||
|
||||
//codec
|
||||
Toolkit::TStreamedMatrixDecoder<CMouseControl>* m_decoder = nullptr;
|
||||
|
||||
#if defined TARGET_OS_Linux
|
||||
::Display* m_pMainDisplay = nullptr;
|
||||
::Window m_oRootWindow;
|
||||
#endif
|
||||
};
|
||||
|
||||
class CMouseControlDesc : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
CString getName() const override { return CString("Mouse Control"); }
|
||||
CString getAuthorName() const override { return CString("Guillaume Gibert"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INSERM"); }
|
||||
CString getShortDescription() const override { return CString("Mouse Control for Feedback"); }
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return CString("Experimental box to move the mouse in x direction with respect to the input value. Only implemented on Linux.");
|
||||
}
|
||||
|
||||
CString getCategory() const override { return CString("Tools"); }
|
||||
CString getVersion() const override { return CString("0.1"); }
|
||||
void release() override { }
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_MouseControl; }
|
||||
IPluginObject* create() override { return new CMouseControl(); }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Amplitude", OV_TypeId_StreamedMatrix);
|
||||
prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
|
||||
prototype.addInputSupport(OV_TypeId_StreamedMatrix);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_MouseControlDesc)
|
||||
};
|
||||
} // namespace Tools
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
// Boxes
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#define OVP_ClassId_MouseControl OpenViBE::CIdentifier(0xDA4B4EEB, 0x64FC6A16)
|
||||
#define OVP_ClassId_MouseControlDesc OpenViBE::CIdentifier(0xB6B65C98, 0xA756ED0E)
|
||||
|
||||
// Global defines
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#ifdef TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
#include "ovp_global_defines.h"
|
||||
#endif // TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
|
||||
#define OV_AttributeId_Box_FlagIsUnstable OpenViBE::CIdentifier(0x666FFFFF, 0x666FFFFF)
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
#include <vector>
|
||||
#include <openvibe/ov_all.h>
|
||||
#include "ovp_defines.h"
|
||||
|
||||
#include "box-algorithms/ovpCMouseControl.h" // inserm
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Tools {
|
||||
|
||||
OVP_Declare_Begin()
|
||||
// @BEGIN inserm
|
||||
OVP_Declare_New(CMouseControlDesc);
|
||||
context.getTypeManager().registerEnumerationEntry(OV_TypeId_BoxAlgorithmFlag, OV_AttributeId_Box_FlagIsUnstable.toString(),
|
||||
OV_AttributeId_Box_FlagIsUnstable.id());
|
||||
// @END inserm
|
||||
|
||||
OVP_Declare_End()
|
||||
|
||||
} // namespace Tools
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
PROJECT(openvibe-plugins-contrib-network-io)
|
||||
|
||||
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
|
||||
SET(PROJECT_VERSION ${OV_GLOBAL_VERSION_STRING})
|
||||
|
||||
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.inl)
|
||||
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
|
||||
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
FOLDER ${PLUGINS_FOLDER}
|
||||
COMPILE_FLAGS "-DOVP_Exports -DOVP_Shared")
|
||||
|
||||
# ---------------------------------
|
||||
INCLUDE("FindOpenViBE")
|
||||
INCLUDE("FindOpenViBECommon")
|
||||
INCLUDE("FindOpenViBEToolkit")
|
||||
# INCLUDE("FindOpenViBEModuleEBML")
|
||||
INCLUDE("FindThirdPartyLSL")
|
||||
|
||||
# -----------------------------
|
||||
# Install files
|
||||
# -----------------------------
|
||||
INSTALL(TARGETS ${PROJECT_NAME}
|
||||
RUNTIME DESTINATION ${DIST_BINDIR}
|
||||
LIBRARY DESTINATION ${DIST_LIBDIR}
|
||||
ARCHIVE DESTINATION ${DIST_LIBDIR})
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* \page BoxAlgorithm_OSCController OSC Controller
|
||||
__________________________________________________________________
|
||||
|
||||
Detailed description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_OSCController_Description|
|
||||
By this box, you can control OSC supporting devices, such as
|
||||
synthesizers and oscillators. It can be used to turn OpenViBE
|
||||
streams into sound, e.g. for brain music or auditory BCI.
|
||||
|
||||
The OSC Controller box simply sends the incoming data as UDP
|
||||
messages to the specified OSC Server. Each message is flagged with
|
||||
an OSC Address specifying the device that the message is intended to
|
||||
control.
|
||||
* |OVP_DocEnd_BoxAlgorithm_OSCController_Description|
|
||||
__________________________________________________________________
|
||||
|
||||
Inputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_OSCController_Inputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_OSCController_Inputs|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_OSCController_Input1|
|
||||
Data to send to the OSC Server. This input can be either a signal, a matrix, or a stimulation. The signal or matrix must have only 1 channel (row). Signals are sent as float and stimulations as size_t.
|
||||
* |OVP_DocEnd_BoxAlgorithm_OSCController_Input1|
|
||||
|
||||
__________________________________________________________________
|
||||
|
||||
Settings description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_OSCController_Settings|
|
||||
* |OVP_DocEnd_BoxAlgorithm_OSCController_Settings|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_OSCController_Setting1|
|
||||
Server address (IP or DNS)
|
||||
* |OVP_DocEnd_BoxAlgorithm_OSCController_Setting1|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_OSCController_Setting2|
|
||||
Server port
|
||||
* |OVP_DocEnd_BoxAlgorithm_OSCController_Setting2|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_OSCController_Setting3|
|
||||
The OSC Address specifying the device the messages are intended to, e.g. /oscillator/4/frequency
|
||||
* |OVP_DocEnd_BoxAlgorithm_OSCController_Setting3|
|
||||
__________________________________________________________________
|
||||
|
||||
Examples description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_OSCController_Examples|
|
||||
Utilities such as OSC DataMonitor by Kasper Kamperman can be used to debug the messages sent out by the box.
|
||||
* |OVP_DocEnd_BoxAlgorithm_OSCController_Examples|
|
||||
__________________________________________________________________
|
||||
|
||||
Miscellaneous description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_OSCController_Miscellaneous|
|
||||
In the current implementation, the data is sent when received. OpenViBE internal timing is not passed to the OSC.
|
||||
The box supports only one input, but the input type can be modified. The input must have only one channel. For signals, you can extract a
|
||||
channel by using the Channel Selector box. Also, if input type is a signal, its sampling rate is ignored.
|
||||
However, several OSC Controller boxes can be used at the same time if multiple sources are needed to be
|
||||
sent to the OSC Server(s) or device(s). These limitations are to keep the code simple.
|
||||
|
||||
The box is not intended to transmit large chunks of numeric data. It may be meaningful to limit the
|
||||
amount of data on the OpenViBE side e.g. with boxes such as Signal Average, Downsampling or Signal Decimation, or Stimulation Filter.
|
||||
|
||||
For more information about the OSC protocol and applications that support it, please see http://www.opensoundcontrol.org
|
||||
|
||||
* |OVP_DocEnd_BoxAlgorithm_OSCController_Miscellaneous|
|
||||
*/
|
||||
+850
@@ -0,0 +1,850 @@
|
||||
/**
|
||||
OSCPKT : a minimalistic OSC ( http://opensoundcontrol.org ) c++ library
|
||||
|
||||
Before using this file please take the time to read the OSC spec, it
|
||||
is short and not complicated: http://opensoundcontrol.org/spec-1_0
|
||||
|
||||
Features:
|
||||
- handles basic OSC types: TFihfdsb
|
||||
- handles bundles
|
||||
- handles OSC pattern-matching rules (wildcards etc in message paths)
|
||||
- portable on win / macos / linux
|
||||
- robust wrt malformed packets
|
||||
- optional udp transport for packets
|
||||
- concise, all in a single .h file
|
||||
- does not throw exceptions
|
||||
|
||||
does not:
|
||||
- take into account timestamp values.
|
||||
- provide a cpu-scalable message dispatching.
|
||||
- not suitable for use inside a realtime thread as it allocates memory when
|
||||
building or reading messages.
|
||||
|
||||
|
||||
There are basically 3 classes of interest:
|
||||
- oscpkt::Message : read/write the content of an OSC message
|
||||
- oscpkt::PacketReader : read the bundles/messages embedded in an OSC packet
|
||||
- oscpkt::PacketWriter : write bundles/messages into an OSC packet
|
||||
|
||||
And optionaly:
|
||||
- oscpkt::UdpSocket : read/write OSC packets over UDP.
|
||||
|
||||
example: oscpkt_demo.cc
|
||||
example: oscpkt_test.cc
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2010 Julien Pommier
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
(this is the zlib license)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <stdint.h>
|
||||
#else
|
||||
namespace oscpkt {
|
||||
typedef __int32 int32_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
}
|
||||
#endif
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#if defined(OSCPKT_OSTREAM_OUTPUT) || defined(OSCPKT_TEST)
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
namespace oscpkt {
|
||||
|
||||
/**
|
||||
OSC timetag stuff, the highest 32-bit are seconds, the lowest are fraction of a second.
|
||||
*/
|
||||
class TimeTag
|
||||
{
|
||||
uint64_t v = 1;
|
||||
public:
|
||||
TimeTag() {}
|
||||
explicit TimeTag(const uint64_t w): v(w) {}
|
||||
operator uint64_t() const { return v; }
|
||||
static TimeTag immediate() { return TimeTag(1); }
|
||||
};
|
||||
|
||||
/* the various types that we handle (OSC 1.0 specifies that INT32/FLOAT/STRING/BLOB are the bare minimum) */
|
||||
enum
|
||||
{
|
||||
TYPE_TAG_TRUE = 'T',
|
||||
TYPE_TAG_FALSE = 'F',
|
||||
TYPE_TAG_INT32 = 'i',
|
||||
TYPE_TAG_INT64 = 'h',
|
||||
TYPE_TAG_FLOAT = 'f',
|
||||
TYPE_TAG_DOUBLE = 'd',
|
||||
TYPE_TAG_STRING = 's',
|
||||
TYPE_TAG_BLOB = 'b'
|
||||
};
|
||||
|
||||
/* a few utility functions follow.. */
|
||||
|
||||
// round to the next multiple of 4, works for size_t and pointer arguments
|
||||
template <typename Type>
|
||||
Type ceil4(Type p) { return Type((size_t(p) + 3) & (~size_t(3))); }
|
||||
|
||||
// check that a memory area is zero padded until the next address which is a multiple of 4
|
||||
inline bool isZeroPaddingCorrect(const char* p)
|
||||
{
|
||||
const char* q = ceil4(p);
|
||||
for (; p < q; ++p) { if (*p != 0) { return false; } }
|
||||
return true;
|
||||
}
|
||||
|
||||
// stuff for reading / writing POD ("Plain Old Data") variables to unaligned bytes.
|
||||
template <typename POD>
|
||||
union PodBytes
|
||||
{
|
||||
char bytes[sizeof(POD)];
|
||||
POD value;
|
||||
};
|
||||
|
||||
inline bool isBigEndian()
|
||||
{ // a compile-time constant would certainly improve performances..
|
||||
PodBytes<int32_t> p;
|
||||
p.value = 0x12345678;
|
||||
return p.bytes[0] == 0x12;
|
||||
}
|
||||
|
||||
/** read unaligned bytes into a POD type, assuming the bytes are a little endian representation */
|
||||
template <typename POD>
|
||||
POD bytes2pod(const char* bytes)
|
||||
{
|
||||
PodBytes<POD> p;
|
||||
for (size_t i = 0; i < sizeof(POD); ++i)
|
||||
{
|
||||
if (isBigEndian()) { p.bytes[i] = bytes[i]; }
|
||||
else { p.bytes[i] = bytes[sizeof(POD) - i - 1]; }
|
||||
}
|
||||
return p.value;
|
||||
}
|
||||
|
||||
/** stored a POD type into an unaligned bytes array, using little endian representation */
|
||||
template <typename POD>
|
||||
void pod2bytes(const POD value, char* bytes)
|
||||
{
|
||||
PodBytes<POD> p;
|
||||
p.value = value;
|
||||
for (size_t i = 0; i < sizeof(POD); ++i)
|
||||
{
|
||||
if (isBigEndian()) { bytes[i] = p.bytes[i]; }
|
||||
else { bytes[i] = p.bytes[sizeof(POD) - i - 1]; }
|
||||
}
|
||||
}
|
||||
|
||||
/** internal stuff, handles the dynamic storage with correct alignments to 4 bytes */
|
||||
struct Storage
|
||||
{
|
||||
std::vector<char> data;
|
||||
Storage() { data.reserve(200); }
|
||||
|
||||
char* getBytes(const size_t sz)
|
||||
{
|
||||
assert((data.size() & 3) == 0);
|
||||
if (data.size() + sz > data.capacity()) { data.reserve((data.size() + sz) * 2); }
|
||||
const size_t sz4 = ceil4(sz);
|
||||
const size_t pos = data.size();
|
||||
data.resize(pos + sz4); // resize will fill with zeros, so the zero padding is OK
|
||||
return &(data[pos]);
|
||||
}
|
||||
|
||||
char* begin() { return !data.empty() ? &data.front() : nullptr; }
|
||||
char* end() { return begin() + size(); }
|
||||
const char* begin() const { return !data.empty() ? &data.front() : nullptr; }
|
||||
const char* end() const { return begin() + size(); }
|
||||
size_t size() const { return data.size(); }
|
||||
void assign(const char* beg, const char* end) { data.assign(beg, end); }
|
||||
void clear() { data.resize(0); }
|
||||
};
|
||||
|
||||
/** check if the path matches the supplied path pattern , according to the OSC spec pattern rules ('*' and '//' wildcards, '{}' alternatives, brackets etc) */
|
||||
bool fullPatternMatch(const std::string& pattern, const std::string& path);
|
||||
/** check if the path matches the beginning of pattern */
|
||||
bool partialPatternMatch(const std::string& pattern, const std::string& path);
|
||||
|
||||
#if defined(OSCPKT_DEBUG)
|
||||
#define OSCPKT_SET_ERR(errcode) do { if (!err) { err = errcode; std::cerr << "set " #errcode << " at line " << __LINE__ << "\n"; } } while (0)
|
||||
#else
|
||||
#define OSCPKT_SET_ERR(errcode) do { if (!err) err = errcode; } while (0)
|
||||
#endif
|
||||
|
||||
typedef enum
|
||||
{
|
||||
OK_NO_ERROR=0,
|
||||
// errors raised by the Message class:
|
||||
MALFORMED_ADDRESS_PATTERN,
|
||||
MALFORMED_TYPE_TAGS,
|
||||
MALFORMED_ARGUMENTS,
|
||||
UNHANDLED_TYPE_TAGS,
|
||||
// errors raised by ArgReader
|
||||
TYPE_MISMATCH,
|
||||
NOT_ENOUGH_ARG,
|
||||
PATTERN_MISMATCH,
|
||||
// errors raised by PacketReader/PacketWriter
|
||||
INVALID_BUNDLE,
|
||||
INVALID_PACKET_SIZE,
|
||||
BUNDLE_REQUIRED_FOR_MULTI_MESSAGES
|
||||
} ErrorCode;
|
||||
|
||||
/**
|
||||
struct used to hold an OSC message that will be written or read.
|
||||
|
||||
The list of arguments is exposed as a sort of queue. You "pop"
|
||||
arguments from the front of the queue when reading, you push
|
||||
arguments at the back of the queue when writing.
|
||||
|
||||
Many functions return *this, so they can be chained: init("/foo").pushInt32(2).pushStr("kllk")...
|
||||
|
||||
Example of use:
|
||||
|
||||
creation of a message:
|
||||
@code
|
||||
msg.init("/foo").pushInt32(4).pushStr("bar");
|
||||
@endcode
|
||||
reading a message, with error detection:
|
||||
@code
|
||||
if (msg.match("/foo/b*ar/plop")) {
|
||||
int i; std::string s; std::vector<char> b;
|
||||
if (msg.arg().popInt32(i).popStr(s).popBlob(b).isOkNoMoreArgs()) {
|
||||
process message...;
|
||||
} else arguments mismatch;
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
class Message
|
||||
{
|
||||
TimeTag time_tag;
|
||||
std::string address;
|
||||
std::string type_tags;
|
||||
std::vector<std::pair<size_t, size_t>> arguments; // array of pairs (pos,size), pos being an index into the 'storage' array.
|
||||
Storage storage; // the arguments data is stored here
|
||||
ErrorCode err;
|
||||
public:
|
||||
/** ArgReader is used for popping arguments from a Message, holds a
|
||||
pointer to the original Message, and maintains a local error code */
|
||||
class ArgReader
|
||||
{
|
||||
const Message* msg;
|
||||
ErrorCode err;
|
||||
size_t arg_idx; // arg index of the next arg that will be popped out.
|
||||
public:
|
||||
ArgReader(const Message& m, const ErrorCode e = OK_NO_ERROR) : msg(&m), err(msg->getErr()), arg_idx(0)
|
||||
{
|
||||
if (e != OK_NO_ERROR && err == OK_NO_ERROR) { err = e; }
|
||||
}
|
||||
|
||||
ArgReader(const ArgReader& other) : msg(other.msg), err(other.err), arg_idx(other.arg_idx) {}
|
||||
bool isBool() { return currentTypeTag() == TYPE_TAG_TRUE || currentTypeTag() == TYPE_TAG_FALSE; }
|
||||
bool isInt32() { return currentTypeTag() == TYPE_TAG_INT32; }
|
||||
bool isInt64() { return currentTypeTag() == TYPE_TAG_INT64; }
|
||||
bool isFloat() { return currentTypeTag() == TYPE_TAG_FLOAT; }
|
||||
bool isDouble() { return currentTypeTag() == TYPE_TAG_DOUBLE; }
|
||||
bool isStr() { return currentTypeTag() == TYPE_TAG_STRING; }
|
||||
bool isBlob() { return currentTypeTag() == TYPE_TAG_BLOB; }
|
||||
|
||||
size_t nbArgRemaining() const { return msg->arguments.size() - arg_idx; }
|
||||
bool isOk() const { return err == OK_NO_ERROR; }
|
||||
operator bool() const { return isOk(); } // implicit bool conversion is handy here
|
||||
/** call this at the end of the popXXX() chain to make sure everything is ok and
|
||||
all arguments have been popped */
|
||||
bool isOkNoMoreArgs() const { return err == OK_NO_ERROR && nbArgRemaining() == 0; }
|
||||
ErrorCode getErr() const { return err; }
|
||||
|
||||
/** retrieve an int32_t argument */
|
||||
ArgReader& popInt32(int32_t& i) { return popPod<int32_t>(TYPE_TAG_INT32, i); }
|
||||
/** retrieve an int64_t argument */
|
||||
ArgReader& popInt64(int64_t& i) { return popPod<int64_t>(TYPE_TAG_INT64, i); }
|
||||
/** retrieve a single precision floating point argument */
|
||||
ArgReader& popFloat(float& f) { return popPod<float>(TYPE_TAG_FLOAT, f); }
|
||||
/** retrieve a double precision floating point argument */
|
||||
ArgReader& popDouble(double& d) { return popPod<double>(TYPE_TAG_DOUBLE, d); }
|
||||
/** retrieve a string argument (no check performed on its content, so it may contain any byte value except 0) */
|
||||
ArgReader& popStr(std::string& s)
|
||||
{
|
||||
if (precheck(TYPE_TAG_STRING)) { s = argBeg(arg_idx++); }
|
||||
return *this;
|
||||
}
|
||||
/** retrieve a binary blob */
|
||||
ArgReader& popBlob(std::vector<char>& b)
|
||||
{
|
||||
if (precheck(TYPE_TAG_BLOB))
|
||||
{
|
||||
b.assign(argBeg(arg_idx) + 4, argEnd(arg_idx));
|
||||
++arg_idx;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
/** retrieve a bool argument */
|
||||
ArgReader& popBool(bool& b)
|
||||
{
|
||||
b = false;
|
||||
if (arg_idx >= msg->arguments.size()) { OSCPKT_SET_ERR(NOT_ENOUGH_ARG); }
|
||||
else if (currentTypeTag() == TYPE_TAG_TRUE) { b = true; }
|
||||
else if (currentTypeTag() == TYPE_TAG_FALSE) { b = false; }
|
||||
else { OSCPKT_SET_ERR(TYPE_MISMATCH); }
|
||||
++arg_idx;
|
||||
return *this;
|
||||
}
|
||||
/** skip whatever comes next */
|
||||
ArgReader& pop()
|
||||
{
|
||||
if (arg_idx >= msg->arguments.size()) { OSCPKT_SET_ERR(NOT_ENOUGH_ARG); }
|
||||
else { ++arg_idx; }
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
const char* argBeg(const size_t idx) const
|
||||
{
|
||||
if (err || idx >= msg->arguments.size()) { return nullptr; }
|
||||
return msg->storage.begin() + msg->arguments[idx].first;
|
||||
}
|
||||
|
||||
const char* argEnd(const size_t idx) const
|
||||
{
|
||||
if (err || idx >= msg->arguments.size()) { return nullptr; }
|
||||
return msg->storage.begin() + msg->arguments[idx].first + msg->arguments[idx].second;
|
||||
}
|
||||
|
||||
int currentTypeTag()
|
||||
{
|
||||
if (!err && arg_idx < msg->type_tags.size()) { return msg->type_tags[arg_idx]; }
|
||||
OSCPKT_SET_ERR(NOT_ENOUGH_ARG);
|
||||
return -1;
|
||||
}
|
||||
|
||||
template <typename POD>
|
||||
ArgReader& popPod(const int tag, POD& v)
|
||||
{
|
||||
if (precheck(tag))
|
||||
{
|
||||
v = bytes2pod<POD>(argBeg(arg_idx));
|
||||
++arg_idx;
|
||||
}
|
||||
else { v = POD(0); }
|
||||
return *this;
|
||||
}
|
||||
/* pre-check stuff before popping an argument from the message */
|
||||
bool precheck(const int tag)
|
||||
{
|
||||
if (arg_idx >= msg->arguments.size()) { OSCPKT_SET_ERR(NOT_ENOUGH_ARG); }
|
||||
else if (!err && currentTypeTag() != tag) { OSCPKT_SET_ERR(TYPE_MISMATCH); }
|
||||
return err == OK_NO_ERROR;
|
||||
}
|
||||
};
|
||||
|
||||
Message() { clear(); }
|
||||
Message(const std::string& s, const TimeTag tt = TimeTag::immediate()) : time_tag(tt), address(s), err(OK_NO_ERROR) {}
|
||||
|
||||
Message(const void* ptr, const size_t sz, const TimeTag tt = TimeTag::immediate())
|
||||
{
|
||||
buildFromRawData(ptr, sz);
|
||||
time_tag = tt;
|
||||
}
|
||||
|
||||
bool isOk() const { return err == OK_NO_ERROR; }
|
||||
ErrorCode getErr() const { return err; }
|
||||
|
||||
/** return the type_tags string, with its initial ',' stripped. */
|
||||
const std::string& typeTags() const { return type_tags; }
|
||||
/** retrieve the address pattern. If you want to follow to the whole OSC spec, you
|
||||
have to handle its matching rules for address specifications -- this file does
|
||||
not provide this functionality */
|
||||
const std::string& addressPattern() const { return address; }
|
||||
TimeTag timeTag() const { return time_tag; }
|
||||
/** clear the message and start a new message with the supplied address and time_tag. */
|
||||
Message& init(const std::string& addr, const TimeTag tt = TimeTag::immediate())
|
||||
{
|
||||
clear();
|
||||
address = addr;
|
||||
time_tag = tt;
|
||||
if (address.empty() || address[0] != '/') { OSCPKT_SET_ERR(MALFORMED_ADDRESS_PATTERN); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** start a matching test. The typical use-case is to follow this by
|
||||
a sequence of calls to popXXX() and a final call to
|
||||
isOkNoMoreArgs() which will allow to check that everything went
|
||||
fine. For example:
|
||||
@code
|
||||
if (msg.match("/foo").popInt32(i).isOkNoMoreArgs()) { blah(i); }
|
||||
else if (msg.match("/bar").popStr(s).popInt32(i).isOkNoMoreArgs()) { plop(s,i); }
|
||||
else std::cerr << "unhandled message: " << msg << "\n";
|
||||
@endcode
|
||||
*/
|
||||
ArgReader match(const std::string& test) const { return ArgReader(*this, fullPatternMatch(address, test) ? OK_NO_ERROR : PATTERN_MISMATCH); }
|
||||
/** return true if the 'test' path matched by the first characters of addressPattern().
|
||||
For ex. ("/foo/bar").partialMatch("/foo/") is true */
|
||||
ArgReader partialMatch(const std::string& test) const { return ArgReader(*this, partialPatternMatch(address, test) ? OK_NO_ERROR : PATTERN_MISMATCH); }
|
||||
|
||||
ArgReader arg() const { return ArgReader(*this, OK_NO_ERROR); }
|
||||
|
||||
/** build the osc message for raw data (the message will keep a copy of that data) */
|
||||
void buildFromRawData(const void* ptr, const size_t sz)
|
||||
{
|
||||
clear();
|
||||
storage.assign((const char*)ptr, (const char*)ptr + sz);
|
||||
const char* address_beg = storage.begin();
|
||||
const char* address_end = (const char*)memchr(address_beg, 0, storage.end() - address_beg);
|
||||
if (!address_end || !isZeroPaddingCorrect(address_end + 1) || address_beg[0] != '/')
|
||||
{
|
||||
OSCPKT_SET_ERR(MALFORMED_ADDRESS_PATTERN);
|
||||
return;
|
||||
}
|
||||
address.assign(address_beg, address_end);
|
||||
|
||||
const char* type_tags_beg = ceil4(address_end + 1);
|
||||
const char* type_tags_end = (const char*)memchr(type_tags_beg, 0, storage.end() - type_tags_beg);
|
||||
if (!type_tags_end || !isZeroPaddingCorrect(type_tags_end + 1) || type_tags_beg[0] != ',')
|
||||
{
|
||||
OSCPKT_SET_ERR(MALFORMED_TYPE_TAGS);
|
||||
return;
|
||||
}
|
||||
type_tags.assign(type_tags_beg + 1, type_tags_end);
|
||||
// we do not copy the initial ','
|
||||
|
||||
const char* arg = ceil4(type_tags_end + 1);
|
||||
assert(arg <= storage.end());
|
||||
size_t iarg = 0;
|
||||
while (isOk() && iarg < type_tags.size())
|
||||
{
|
||||
assert(arg <= storage.end());
|
||||
size_t len = getArgSize(type_tags[iarg], arg);
|
||||
if (isOk()) { arguments.push_back(std::make_pair(arg - storage.begin(), len)); }
|
||||
arg += ceil4(len);
|
||||
++iarg;
|
||||
}
|
||||
if (iarg < type_tags.size() || arg != storage.end()) { OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); }
|
||||
}
|
||||
|
||||
/* below are all the functions that serve when *writing* a message */
|
||||
Message& pushBool(const bool b)
|
||||
{
|
||||
type_tags += (b ? TYPE_TAG_TRUE : TYPE_TAG_FALSE);
|
||||
arguments.push_back(std::make_pair(storage.size(), storage.size()));
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& pushInt32(const int32_t i) { return pushPod(TYPE_TAG_INT32, i); }
|
||||
Message& pushInt64(const int64_t h) { return pushPod(TYPE_TAG_INT64, h); }
|
||||
Message& pushFloat(const float f) { return pushPod(TYPE_TAG_FLOAT, f); }
|
||||
Message& pushDouble(const double d) { return pushPod(TYPE_TAG_DOUBLE, d); }
|
||||
|
||||
Message& pushStr(const std::string& s)
|
||||
{
|
||||
assert(s.size() < 2147483647); // insane values are not welcome
|
||||
type_tags += TYPE_TAG_STRING;
|
||||
arguments.push_back(std::make_pair(storage.size(), s.size() + 1));
|
||||
strcpy(storage.getBytes(s.size() + 1), s.c_str());
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& pushBlob(void* ptr, const size_t size)
|
||||
{
|
||||
assert(size < 2147483647); // insane values are not welcome
|
||||
type_tags += TYPE_TAG_BLOB;
|
||||
arguments.push_back(std::make_pair(storage.size(), size + 4));
|
||||
pod2bytes<int32_t>(int32_t(size), storage.getBytes(4));
|
||||
if (size) { memcpy(storage.getBytes(size), ptr, size); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** reset the message to a clean state */
|
||||
void clear()
|
||||
{
|
||||
address.clear();
|
||||
type_tags.clear();
|
||||
storage.clear();
|
||||
arguments.clear();
|
||||
err = OK_NO_ERROR;
|
||||
time_tag = TimeTag::immediate();
|
||||
}
|
||||
|
||||
/** write the raw message data (used by PacketWriter) */
|
||||
void packMessage(Storage& s, const bool size) const
|
||||
{
|
||||
if (!isOk()) { return; }
|
||||
const size_t addr = address.size() + 1;
|
||||
const size_t type = type_tags.size() + 2;
|
||||
if (size) { pod2bytes<uint32_t>(uint32_t(ceil4(addr) + ceil4(type) + ceil4(storage.size())), s.getBytes(4)); }
|
||||
strcpy(s.getBytes(addr), address.c_str());
|
||||
strcpy(s.getBytes(type), ("," + type_tags).c_str());
|
||||
if (storage.size()) { memcpy(s.getBytes(storage.size()), const_cast<Storage&>(storage).begin(), storage.size()); }
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/* get the number of bytes occupied by the argument */
|
||||
size_t getArgSize(const int type, const char* p)
|
||||
{
|
||||
if (err) { return 0; }
|
||||
size_t sz = 0;
|
||||
assert(p >= storage.begin() && p <= storage.end());
|
||||
switch (type)
|
||||
{
|
||||
case TYPE_TAG_TRUE:
|
||||
case TYPE_TAG_FALSE: sz = 0;
|
||||
break;
|
||||
case TYPE_TAG_INT32:
|
||||
case TYPE_TAG_FLOAT: sz = 4;
|
||||
break;
|
||||
case TYPE_TAG_INT64:
|
||||
case TYPE_TAG_DOUBLE: sz = 8;
|
||||
break;
|
||||
case TYPE_TAG_STRING:
|
||||
{
|
||||
const char* q = (const char*)memchr(p, 0, storage.end() - p);
|
||||
if (!q) { OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); }
|
||||
else { sz = (q - p) + 1; }
|
||||
}
|
||||
break;
|
||||
case TYPE_TAG_BLOB:
|
||||
{
|
||||
if (p == storage.end())
|
||||
{
|
||||
OSCPKT_SET_ERR(MALFORMED_ARGUMENTS);
|
||||
return 0;
|
||||
}
|
||||
sz = 4 + bytes2pod<uint32_t>(p);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
OSCPKT_SET_ERR(UNHANDLED_TYPE_TAGS);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (p + sz > storage.end() || /* string or blob too large.. */
|
||||
p + sz < p /* or even blob so large that it did overflow */)
|
||||
{
|
||||
OSCPKT_SET_ERR(MALFORMED_ARGUMENTS);
|
||||
return 0;
|
||||
}
|
||||
if (!isZeroPaddingCorrect(p + sz))
|
||||
{
|
||||
OSCPKT_SET_ERR(MALFORMED_ARGUMENTS);
|
||||
return 0;
|
||||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
template <typename POD>
|
||||
Message& pushPod(const int tag, POD v)
|
||||
{
|
||||
type_tags += char(tag);
|
||||
arguments.push_back(std::make_pair(storage.size(), sizeof(POD)));
|
||||
pod2bytes(v, storage.getBytes(sizeof(POD)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef OSCPKT_OSTREAM_OUTPUT
|
||||
friend std::ostream &operator<<(std::ostream &os, const Message &msg) {
|
||||
os << "osc_address: '" << msg.address << "', types: '" << msg.type_tags << "', timetag=" << msg.time_tag << ", args=[";
|
||||
Message::ArgReader arg(msg);
|
||||
while (arg.nbArgRemaining() && arg.isOk()) {
|
||||
if (arg.isBool()) { bool b; arg.popBool(b); os << (b?"True":"False"); }
|
||||
else if (arg.isInt32()) { int32_t i; arg.popInt32(i); os << i; }
|
||||
else if (arg.isInt64()) { int64_t h; arg.popInt64(h); os << h << "ll"; }
|
||||
else if (arg.isFloat()) { float f; arg.popFloat(f); os << f << "f"; }
|
||||
else if (arg.isDouble()) { double d; arg.popDouble(d); os << d; }
|
||||
else if (arg.isStr()) { std::string s; arg.popStr(s); os << "'" << s << "'"; }
|
||||
else if (arg.isBlob()) { std::vector<char> b; arg.popBlob(b); os << "Blob " << b.size() << " bytes"; }
|
||||
else {
|
||||
assert(0); // I forgot a case..
|
||||
}
|
||||
if (arg.nbArgRemaining()) os << ", ";
|
||||
}
|
||||
if (!arg.isOk()) { os << " ERROR#" << arg.getErr(); }
|
||||
os << "]";
|
||||
return os;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
parse an OSC packet and extracts the embedded OSC messages.
|
||||
*/
|
||||
class PacketReader
|
||||
{
|
||||
public:
|
||||
PacketReader() { err = OK_NO_ERROR; }
|
||||
/** pointer and size of the osc packet to be parsed. */
|
||||
PacketReader(const void* ptr, size_t sz) { init(ptr, sz); }
|
||||
|
||||
void init(const void* ptr, const size_t sz)
|
||||
{
|
||||
err = OK_NO_ERROR;
|
||||
messages.clear();
|
||||
if ((sz % 4) == 0) { parse((const char*)ptr, (const char*)ptr + sz, TimeTag::immediate()); }
|
||||
else { OSCPKT_SET_ERR(INVALID_PACKET_SIZE); }
|
||||
it_messages = messages.begin();
|
||||
}
|
||||
|
||||
/** extract the next osc message from the packet. return 0 when all messages have been read, or in case of error. */
|
||||
Message* popMessage()
|
||||
{
|
||||
if (!err && !messages.empty() && it_messages != messages.end()) { return &*it_messages++; }
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool isOk() const { return err == OK_NO_ERROR; }
|
||||
ErrorCode getErr() const { return err; }
|
||||
|
||||
private:
|
||||
std::list<Message> messages;
|
||||
std::list<Message>::iterator it_messages;
|
||||
ErrorCode err;
|
||||
|
||||
void parse(const char* beg, const char* end, const TimeTag time_tag)
|
||||
{
|
||||
assert(beg <= end && !err);
|
||||
assert(((end-beg)%4)==0);
|
||||
|
||||
if (beg == end) { return; }
|
||||
if (*beg == '#')
|
||||
{
|
||||
/* it's a bundle */
|
||||
if (end - beg >= 20
|
||||
&& memcmp(beg, "#bundle\0", 8) == 0)
|
||||
{
|
||||
const TimeTag timeTag2(bytes2pod<uint64_t>(beg + 8));
|
||||
const char* pos = beg + 16;
|
||||
do
|
||||
{
|
||||
const uint32_t sz = bytes2pod<uint32_t>(pos);
|
||||
pos += 4;
|
||||
if ((sz & 3) != 0 || pos + sz > end || pos + sz < pos) { OSCPKT_SET_ERR(INVALID_BUNDLE); }
|
||||
else
|
||||
{
|
||||
parse(pos, pos + sz, timeTag2);
|
||||
pos += sz;
|
||||
}
|
||||
} while (!err && pos != end);
|
||||
}
|
||||
else { OSCPKT_SET_ERR(INVALID_BUNDLE); }
|
||||
}
|
||||
else
|
||||
{
|
||||
messages.push_back(Message(beg, end - beg, time_tag));
|
||||
if (!messages.back().isOk()) { OSCPKT_SET_ERR(messages.back().getErr()); }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Assemble messages into an OSC packet. Example of use:
|
||||
@code
|
||||
PacketWriter pkt;
|
||||
Message msg;
|
||||
pkt.startBundle();
|
||||
pkt.addMessage(msg.init("/foo").pushBool(true).pushStr("plop").pushFloat(3.14f));
|
||||
pkt.addMessage(msg.init("/bar").pushBool(false));
|
||||
pkt.endBundle();
|
||||
if (pkt.isOk()) {
|
||||
send(pkt.data(), pkt.size());
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
class PacketWriter
|
||||
{
|
||||
public:
|
||||
PacketWriter() { init(); }
|
||||
|
||||
PacketWriter& init()
|
||||
{
|
||||
err = OK_NO_ERROR;
|
||||
storage.clear();
|
||||
bundles.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** begin a new bundle. If you plan to pack more than one message in the Osc packet, you have to
|
||||
put them in a bundle. Nested bundles inside bundles are also allowed. */
|
||||
PacketWriter& startBundle(const TimeTag ts = TimeTag::immediate())
|
||||
{
|
||||
char* p;
|
||||
if (!bundles.empty()) { p = storage.getBytes(4); } // hold the bundle size
|
||||
p = storage.getBytes(8);
|
||||
strcpy(p, "#bundle");
|
||||
bundles.push_back(p - storage.begin());
|
||||
p = storage.getBytes(8);
|
||||
pod2bytes<uint64_t>(ts, p);
|
||||
return *this;
|
||||
}
|
||||
/** close the current bundle. */
|
||||
PacketWriter& endBundle()
|
||||
{
|
||||
if (!bundles.empty())
|
||||
{
|
||||
if (storage.size() - bundles.back() == 16) { pod2bytes<uint32_t>(0, storage.getBytes(4)); } // the 'empty bundle' case, not very elegant
|
||||
if (bundles.size() > 1) // no size stored for the top-level bundle
|
||||
{
|
||||
pod2bytes<uint32_t>(uint32_t(storage.size() - bundles.back()), storage.begin() + bundles.back() - 4);
|
||||
}
|
||||
bundles.pop_back();
|
||||
}
|
||||
else { OSCPKT_SET_ERR(INVALID_BUNDLE); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** insert an Osc message into the current bundle / packet.
|
||||
*/
|
||||
PacketWriter& addMessage(const Message& msg)
|
||||
{
|
||||
if (storage.size() != 0 && bundles.empty()) { OSCPKT_SET_ERR(BUNDLE_REQUIRED_FOR_MULTI_MESSAGES); }
|
||||
else { msg.packMessage(storage, !bundles.empty()); }
|
||||
if (!msg.isOk()) { OSCPKT_SET_ERR(msg.getErr()); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** the error flag will be raised if an opened bundle is not closed, or if more than one message is
|
||||
inserted in the packet without a bundle */
|
||||
bool isOk() const { return err == OK_NO_ERROR; }
|
||||
ErrorCode getErr() const { return err; }
|
||||
|
||||
/** return the number of bytes of the osc packet -- will always be a
|
||||
multiple of 4 -- returns 0 if the construction of the packet has
|
||||
failed. */
|
||||
size_t packetSize() const { return err ? 0 : storage.size(); }
|
||||
|
||||
/** return the bytes of the osc packet (NULL if the construction of the packet has failed) */
|
||||
char* packetData() { return err ? nullptr : storage.begin(); }
|
||||
private:
|
||||
std::vector<size_t> bundles; // hold the position in the storage array of the beginning marker of each bundle
|
||||
Storage storage;
|
||||
ErrorCode err;
|
||||
};
|
||||
|
||||
// see the OSC spec for the precise pattern matching rules
|
||||
inline const char* internalPatternMatch(const char* pattern, const char* path)
|
||||
{
|
||||
while (*pattern)
|
||||
{
|
||||
const char* p = pattern;
|
||||
if (*p == '?' && *path)
|
||||
{
|
||||
++p;
|
||||
++path;
|
||||
}
|
||||
else if (*p == '[' && *path)
|
||||
{ // bracketted range, e.g. [a-zABC]
|
||||
++p;
|
||||
bool reverse = false;
|
||||
if (*p == '!')
|
||||
{
|
||||
reverse = true;
|
||||
++p;
|
||||
}
|
||||
bool match = reverse;
|
||||
for (; *p && *p != ']'; ++p)
|
||||
{
|
||||
const char c0 = *p;
|
||||
char c1 = c0;
|
||||
if (p[1] == '-' && p[2])
|
||||
{
|
||||
p += 2;
|
||||
c1 = *p;
|
||||
}
|
||||
if (*path >= c0 && *path <= c1) { match = !reverse; }
|
||||
}
|
||||
if (!match || *p != ']') { return pattern; }
|
||||
++p;
|
||||
++path;
|
||||
}
|
||||
else if (*p == '*')
|
||||
{ // wildcard '*'
|
||||
while (*p == '*') { ++p; }
|
||||
const char* best = nullptr;
|
||||
while (true)
|
||||
{
|
||||
const char* ret = internalPatternMatch(p, path);
|
||||
if (ret && ret > best) { best = ret; }
|
||||
if (*path == 0 || *path == '/') { break; }
|
||||
++path;
|
||||
}
|
||||
return best;
|
||||
}
|
||||
else if (*p == '/' && *(p + 1) == '/')
|
||||
{ // the super-wildcard '//'
|
||||
while (*(p + 1) == '/') { ++p; }
|
||||
const char* best = nullptr;
|
||||
while (true)
|
||||
{
|
||||
const char* ret = internalPatternMatch(p, path);
|
||||
if (ret && ret > best) { best = ret; }
|
||||
if (*path == 0) { break; }
|
||||
if (*path == 0 || (path = strchr(path + 1, '/')) == nullptr) { break; }
|
||||
}
|
||||
return best;
|
||||
}
|
||||
else if (*p == '{')
|
||||
{ // braced list {foo,bar,baz}
|
||||
const char *end = strchr(p, '}'), *q;
|
||||
if (!end) { return nullptr; } // syntax error in brace list..
|
||||
bool match = false;
|
||||
do
|
||||
{
|
||||
++p;
|
||||
q = strchr(p, ',');
|
||||
if (q == nullptr || q > end) { q = end; }
|
||||
if (strncmp(p, path, q - p) == 0)
|
||||
{
|
||||
path += (q - p);
|
||||
p = end + 1;
|
||||
match = true;
|
||||
}
|
||||
else { p = q; }
|
||||
} while (q != end && !match);
|
||||
if (!match) { return pattern; }
|
||||
}
|
||||
else if (*p == *path)
|
||||
{
|
||||
++p;
|
||||
++path;
|
||||
} // any other character
|
||||
else { break; }
|
||||
pattern = p;
|
||||
}
|
||||
return (*path == 0 ? pattern : nullptr);
|
||||
}
|
||||
|
||||
inline bool partialPatternMatch(const std::string& pattern, const std::string& path)
|
||||
{
|
||||
const char* q = internalPatternMatch(pattern.c_str(), path.c_str());
|
||||
return q != nullptr;
|
||||
}
|
||||
|
||||
inline bool fullPatternMatch(const std::string& pattern, const std::string& path)
|
||||
{
|
||||
const char* q = internalPatternMatch(pattern.c_str(), path.c_str());
|
||||
return q && *q == 0;
|
||||
}
|
||||
} // namespace oscpkt
|
||||
+345
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
This file provides a dumb c++ wrapper for sending OSC packets over UDP.
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2010 Julien Pommier
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
(this is the zlib license)
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(_MSC_VER) || defined(WIN32)
|
||||
/*
|
||||
if windows.h has been already included, be prepared for tons of
|
||||
compile errors. winsock2 must be included BEFORE windows.h . -- OR
|
||||
define WIN32_LEAN_AND_MEAN before the first #include <windows.h> to
|
||||
prevent it from including tons of crap (winsock.h etc)
|
||||
*/
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
#if defined(_MSC_VER)
|
||||
# pragma comment(lib, "ws2_32.lib")
|
||||
#endif
|
||||
#else
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cerrno>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace oscpkt {
|
||||
|
||||
/** a wrapper class for holding an ip address, mostly used internnally */
|
||||
class SockAddr
|
||||
{
|
||||
union
|
||||
{
|
||||
sockaddr_storage ss; // hold an IPv4 or IPv6 address
|
||||
struct sockaddr sa;
|
||||
} addr_;
|
||||
|
||||
public:
|
||||
|
||||
struct sockaddr& addr() { return addr_.sa; }
|
||||
const struct sockaddr& addr() const { return addr_.sa; }
|
||||
size_t maxLen() const { return sizeof addr_; }
|
||||
|
||||
size_t actualLen() const
|
||||
{
|
||||
if (addr().sa_family == AF_UNSPEC) { return 0; }
|
||||
if (addr().sa_family == AF_INET) { return sizeof(struct sockaddr_in); }
|
||||
if (addr().sa_family == AF_INET6) { return sizeof(struct sockaddr_in6); }
|
||||
return sizeof addr_;
|
||||
}
|
||||
|
||||
SockAddr() { memset(&addr_, 0, sizeof addr_); }
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return addr().sa_family == AF_UNSPEC; /* this is the 0 value */
|
||||
}
|
||||
/** retrieve the current port number, -1 in case of error */
|
||||
int getPort() const
|
||||
{
|
||||
char servname[512];
|
||||
const int err = getnameinfo(&addr_.sa, sizeof addr_, nullptr, 0, servname, sizeof servname, NI_NUMERICSERV);
|
||||
return (err == 0 ? atoi(servname) : -1);
|
||||
}
|
||||
/* convert to a string representation (ip:port) */
|
||||
std::string asString() const
|
||||
{
|
||||
std::string s;
|
||||
if (addr().sa_family)
|
||||
{
|
||||
char hostname[512], servname[512];
|
||||
const int err = getnameinfo(&addr_.sa, sizeof addr_, hostname, sizeof hostname, servname, sizeof servname, NI_NUMERICHOST | NI_NUMERICSERV);
|
||||
if (err == 0)
|
||||
{
|
||||
s = hostname;
|
||||
s += ":";
|
||||
s += servname;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/* NOTE: This breaks build with OpenViBE
|
||||
friend std::ostream &operator<<(std::ostream &os, const SockAddr &ip) {
|
||||
os << "[";
|
||||
switch (ip.addr().sa_family) {
|
||||
case AF_UNSPEC: os << "AF_UNSPEC"; break;
|
||||
case AF_INET: os << "IPv4"; break;
|
||||
case AF_INET6: os << "IPv6"; break;
|
||||
default: os << "unknown family '" << ip.addr().sa_family << "'"; break;
|
||||
}
|
||||
os << " " << ip.asString() << "]";
|
||||
return os;
|
||||
}*/
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
just a wrapper over the classical socket stuff
|
||||
|
||||
should be robust, simple to use, IPv6 ready (avoids all deprecated
|
||||
stuff such as gethostbyname etc), and portable (mac/linux/windows)
|
||||
|
||||
Try to avoid sending packets larger than 8192 because some other
|
||||
implementation may truncate them (python's DatagramRequestHandler
|
||||
of OSC.py for example).
|
||||
*/
|
||||
struct UdpSocket
|
||||
{
|
||||
std::string error_message;
|
||||
int handle; /* the file descriptor for the socket */
|
||||
SockAddr local_addr /* initialised only for bound sockets */;
|
||||
SockAddr remote_addr; /* initialised for connected sockets. Also updated for bound sockets after each datagram received */
|
||||
|
||||
std::vector<char> buffer;
|
||||
|
||||
UdpSocket() : handle(-1)
|
||||
{
|
||||
#ifdef WIN32
|
||||
WSADATA wsa_data;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) { setErr("winsock failed to initialise"); }
|
||||
#endif
|
||||
}
|
||||
|
||||
~UdpSocket()
|
||||
{
|
||||
close();
|
||||
#ifdef WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
if (handle != -1)
|
||||
{
|
||||
#ifdef WIN32
|
||||
closesocket(handle);
|
||||
#else
|
||||
::close(handle);
|
||||
#endif
|
||||
handle = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool isOk() const { return error_message.empty(); }
|
||||
const std::string& errorMessage() const { return error_message; }
|
||||
|
||||
bool isBound() const { return !local_addr.empty(); }
|
||||
int boundPort() const { return local_addr.getPort(); }
|
||||
|
||||
std::string boundPortAsString() const
|
||||
{
|
||||
char s[512];
|
||||
#ifndef _MSC_VER
|
||||
snprintf(s, 512, "%d", boundPort());
|
||||
#else
|
||||
_snprintf_s(s, 512, 512, "%d", boundPort());
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
|
||||
int socketHandle() const { return handle; }
|
||||
|
||||
static std::string localHostName()
|
||||
{
|
||||
/* this stuff is not very nice but this is what liblo does in order to
|
||||
find out a sensible name for the local host */
|
||||
char hostname_buf[512];
|
||||
if (gethostname(hostname_buf, sizeof hostname_buf) != 0) { hostname_buf[0] = 0; }
|
||||
hostname_buf[sizeof hostname_buf - 1] = 0;
|
||||
struct hostent* host = gethostbyname(hostname_buf);
|
||||
if (host) { return host->h_name; }
|
||||
return hostname_buf[0] ? hostname_buf : "localhost";
|
||||
}
|
||||
|
||||
std::string localHostNameWithPort() const { return (localHostName() + ":") + boundPortAsString(); }
|
||||
|
||||
enum
|
||||
{
|
||||
OPTION_UNSPEC = 0,
|
||||
OPTION_FORCE_IPV4 = 1,
|
||||
OPTION_FORCE_IPV6 = 2,
|
||||
OPTION_DEFAULT = OPTION_FORCE_IPV4 // according to liblo's README, using ipv6 sockets causes issues with other non-ipv6 enabled osc software
|
||||
};
|
||||
|
||||
/** open the socket, and prepare for sending datagrams to the specified host:port */
|
||||
bool connectTo(const std::string& host, const std::string& port, const int options = OPTION_DEFAULT) { return openSocket(host, port, options); }
|
||||
|
||||
bool connectTo(const std::string& host, const int port, const int options = OPTION_DEFAULT) { return openSocket(host, port, options); }
|
||||
|
||||
void setErr(const std::string& msg) { if (error_message.empty()) { error_message = msg; } }
|
||||
|
||||
void* packetData() { return buffer.empty() ? nullptr : &buffer[0]; }
|
||||
size_t packetSize() const { return buffer.size(); }
|
||||
SockAddr& packetOrigin() { return remote_addr; }
|
||||
|
||||
bool sendPacket(const void* ptr, const size_t sz) { return sendPacketTo(ptr, sz, remote_addr); }
|
||||
|
||||
bool sendPacketTo(const void* ptr, const size_t sz, SockAddr& addr)
|
||||
{
|
||||
if (!isOk() || handle == -1)
|
||||
{
|
||||
setErr("not opened..");
|
||||
return false;
|
||||
}
|
||||
if (!ptr || sz == 0) { return false; }
|
||||
|
||||
int sent = 0;
|
||||
do
|
||||
{
|
||||
int res;
|
||||
errno = 0;
|
||||
if (isBound()) { res = sendto(handle, (const char*)ptr, int(sz), 0, &addr.addr(), int(addr.actualLen())); }
|
||||
else
|
||||
{
|
||||
res = send(handle, (const char*)ptr, int(sz), 0);
|
||||
// res = write(handle, ptr, sz);
|
||||
}
|
||||
#ifdef WIN32
|
||||
if (res == -1 && WSAGetLastError() == WSAEINTR) { continue; }
|
||||
sent = res;
|
||||
#else
|
||||
//if (res == -1) std::cerr << "sendto handle=" << handle << ", res:" << res << ", sz=" << sz << ", errno=" << errno << " " << strerror(errno) << "\n";
|
||||
if (res == -1 && errno == EINTR) continue;
|
||||
else sent = res;
|
||||
#endif
|
||||
} while (false);
|
||||
|
||||
return size_t(sent) == sz;
|
||||
}
|
||||
|
||||
private:
|
||||
bool openSocket(const std::string& hostname, const int port, const int options)
|
||||
{
|
||||
char port_string[64];
|
||||
#ifdef _MSC_VER
|
||||
_snprintf_s(port_string, 64, 64, "%d", port);
|
||||
#else
|
||||
snprintf(port_string, 64, "%d", port);
|
||||
#endif
|
||||
return openSocket(hostname, port_string, options);
|
||||
}
|
||||
|
||||
bool openSocket(const std::string& hostname, const std::string& port, const int options)
|
||||
{
|
||||
const bool binding = hostname.empty();
|
||||
close();
|
||||
error_message.clear();
|
||||
|
||||
struct addrinfo hints;
|
||||
struct addrinfo* result = nullptr;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
if (options == OPTION_FORCE_IPV4) { hints.ai_family = AF_INET; }
|
||||
else if (options == OPTION_FORCE_IPV6) { hints.ai_family = AF_INET6; }
|
||||
else { hints.ai_family = AF_UNSPEC; } // Allow IPv4 or IPv6 -- in case of problem, try with AF_INET ...
|
||||
hints.ai_socktype = SOCK_DGRAM; // Datagram socket
|
||||
hints.ai_flags = (binding ? AI_PASSIVE : 0); // AI_PASSIVE means socket address is intended for bind
|
||||
|
||||
const int err = getaddrinfo(binding ? nullptr : hostname.c_str(), port.empty() ? nullptr : port.c_str(), &hints, &result);
|
||||
if (err != 0)
|
||||
{
|
||||
setErr(gai_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
struct addrinfo* rp = result;
|
||||
for (; rp && handle == -1; rp = rp->ai_next)
|
||||
{
|
||||
handle = socket(rp->ai_family, rp->ai_socktype,
|
||||
rp->ai_protocol);
|
||||
if (handle == -1) { continue; }
|
||||
|
||||
if (binding)
|
||||
{
|
||||
if (bind(handle, rp->ai_addr, socklen_t(rp->ai_addrlen)) != 0) { close(); }
|
||||
else
|
||||
{
|
||||
socklen_t len = socklen_t(local_addr.maxLen());
|
||||
if (getsockname(handle, &local_addr.addr(), &len) == 0)
|
||||
{
|
||||
/* great */
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (connect(handle, rp->ai_addr, socklen_t(rp->ai_addrlen)) != 0) { close(); }
|
||||
else
|
||||
{
|
||||
assert(size_t(rp->ai_addrlen) <= sizeof remote_addr);
|
||||
memcpy(&remote_addr.addr(), rp->ai_addr, rp->ai_addrlen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
freeaddrinfo(result);
|
||||
result = nullptr;
|
||||
|
||||
if (!rp)
|
||||
{ // we failed miserably
|
||||
setErr(binding ? "bind failed" : "connect failed");
|
||||
assert(handle == -1);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
} // namespace oscpkt
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
#include "ovpCBoxAlgorithmOSCController.h"
|
||||
|
||||
// #include <iostream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace NetworkIO {
|
||||
|
||||
bool CBoxAlgorithmOSCController::initialize()
|
||||
{
|
||||
const CString address = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
const uint64_t port = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
|
||||
m_oscAddress = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2);
|
||||
|
||||
const char* tmp = m_oscAddress.toASCIIString();
|
||||
if (!tmp || !tmp[0] || tmp[0] != '/')
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "OSC Address must start with a '/'\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Connect the socket
|
||||
const std::string str = std::string(address.toASCIIString());
|
||||
m_udpSocket.connectTo(str, uint32_t(port));
|
||||
|
||||
if (!m_udpSocket.isOk())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Error connecting to socket\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get appropriate decoder
|
||||
CIdentifier streamType;
|
||||
this->getStaticBoxContext().getInputType(0, streamType);
|
||||
|
||||
m_decoder = nullptr;
|
||||
if (this->getTypeManager().isDerivedFromStream(streamType,OV_TypeId_StreamedMatrix))
|
||||
{
|
||||
m_decoder = &this->getAlgorithmManager().getAlgorithm(
|
||||
this->getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StreamedMatrixDecoder));
|
||||
}
|
||||
else if (streamType == OV_TypeId_Stimulations)
|
||||
{
|
||||
m_decoder = &this->getAlgorithmManager().getAlgorithm(
|
||||
this->getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StimulationDecoder));
|
||||
}
|
||||
else
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Unsupported type\n";
|
||||
return false;
|
||||
}
|
||||
m_decoder->initialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmOSCController::uninitialize()
|
||||
{
|
||||
if (m_udpSocket.isOk()) { m_udpSocket.close(); }
|
||||
|
||||
if (m_decoder)
|
||||
{
|
||||
this->getAlgorithmManager().releaseAlgorithm(*m_decoder);
|
||||
m_decoder = nullptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmOSCController::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmOSCController::process()
|
||||
{
|
||||
// the dynamic box context describes the current state of the box inputs and outputs (i.e. the chunks)
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
CIdentifier streamType;
|
||||
this->getStaticBoxContext().getInputType(0, streamType);
|
||||
|
||||
oscpkt::PacketWriter pw;
|
||||
oscpkt::Message msg;
|
||||
bool haveData = false;
|
||||
|
||||
for (size_t j = 0; j < boxContext.getInputChunkCount(0); ++j)
|
||||
{
|
||||
if (this->getTypeManager().isDerivedFromStream(streamType,OV_TypeId_StreamedMatrix))
|
||||
{
|
||||
Kernel::TParameterHandler<const IMemoryBuffer*> ip_buffer(
|
||||
m_decoder->getInputParameter(OVP_GD_Algorithm_StreamedMatrixDecoder_InputParameterId_MemoryBufferToDecode));
|
||||
Kernel::TParameterHandler<const CMatrix*> op_pMatrix(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputParameterId_Matrix));
|
||||
|
||||
ip_buffer = boxContext.getInputChunk(0, j);
|
||||
m_decoder->process();
|
||||
|
||||
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputTriggerId_ReceivedBuffer))
|
||||
{
|
||||
// Check that the dimensions are acceptable
|
||||
const CMatrix* matrix = op_pMatrix;
|
||||
if (matrix->getDimensionCount() < 1 || matrix->getDimensionCount() > 2)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Only matrixes of 1 or 2 dimensions are supported\n";
|
||||
return false;
|
||||
}
|
||||
if (matrix->getDimensionCount() == 2 && matrix->getDimensionSize(0) != 1)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "The matrix should have only 1 channel. Use e.g. Channel Selector to prune\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!haveData)
|
||||
{
|
||||
haveData = true;
|
||||
pw.startBundle();
|
||||
}
|
||||
|
||||
for (size_t k = 0; k < matrix->getBufferElementCount(); ++k)
|
||||
{
|
||||
const float inputVal = float(matrix->getBuffer()[k]);
|
||||
pw.addMessage(msg.init(m_oscAddress.toASCIIString()).pushFloat(inputVal));
|
||||
// std::cout << "Add float " << inputVal << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (streamType == OV_TypeId_Stimulations)
|
||||
{
|
||||
Kernel::TParameterHandler<const IMemoryBuffer*> ip_buffer(
|
||||
m_decoder->getInputParameter(OVP_GD_Algorithm_StimulationDecoder_InputParameterId_MemoryBufferToDecode));
|
||||
const Kernel::TParameterHandler<const IStimulationSet*> op_pStimulationSet(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_StimulationDecoder_OutputParameterId_StimulationSet));
|
||||
|
||||
ip_buffer = boxContext.getInputChunk(0, j);
|
||||
m_decoder->process();
|
||||
|
||||
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_StimulationDecoder_OutputTriggerId_ReceivedBuffer))
|
||||
{
|
||||
if (!haveData)
|
||||
{
|
||||
haveData = true;
|
||||
pw.startBundle();
|
||||
}
|
||||
|
||||
for (size_t k = 0; k < op_pStimulationSet->getStimulationCount(); ++k)
|
||||
{
|
||||
const uint64_t stimulus = op_pStimulationSet->getStimulationIdentifier(k);
|
||||
pw.addMessage(msg.init(m_oscAddress.toASCIIString()).pushInt32(int32_t(stimulus)));
|
||||
// std::cout << "Add stimulus " << stimulus << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Unknown stream type\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
boxContext.markInputAsDeprecated(0, j);
|
||||
}
|
||||
|
||||
if (haveData)
|
||||
{
|
||||
pw.endBundle();
|
||||
if (!m_udpSocket.sendPacket(pw.packetData(), pw.packetSize())) { this->getLogManager() << Kernel::LogLevel_Warning << "Error sending out UDP packet\n"; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace NetworkIO
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "oscpkt.h"
|
||||
#include "oscpkt_udp.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace NetworkIO {
|
||||
/**
|
||||
* \class CBoxAlgorithmOSCController
|
||||
* \author Ozan Caglayan (Galatasaray University)
|
||||
* \date Thu May 8 20:57:24 2014
|
||||
* \brief The class CBoxAlgorithmOSCController describes the box OSC Controller.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmOSCController final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
//Here is the different process callbacks possible
|
||||
// - On new input received (the most common behaviour for signal processing) :
|
||||
bool processInput(const size_t index) override;
|
||||
|
||||
bool process() override;
|
||||
|
||||
// As we do with any class in openvibe, we use the macro below
|
||||
// to associate this box to an unique identifier.
|
||||
// The inheritance information is also made available,
|
||||
// as we provide the superclass Toolkit::TBoxAlgorithm < IBoxAlgorithm >
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_OSCController)
|
||||
|
||||
private:
|
||||
|
||||
// Decodes the stream
|
||||
Kernel::IAlgorithmProxy* m_decoder = nullptr;
|
||||
|
||||
// UDP Socket (oscpkt_udp.h)
|
||||
oscpkt::UdpSocket m_udpSocket;
|
||||
|
||||
// OSC Address to some device
|
||||
CString m_oscAddress;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class CBoxAlgorithmOSCControllerDesc
|
||||
* \author Ozan Caglayan (Galatasaray University)
|
||||
* \date Thu May 8 20:57:24 2014
|
||||
* \brief Descriptor of the box OSC Controller.
|
||||
*
|
||||
*/
|
||||
class CBoxAlgorithmOSCControllerDesc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("OSC Controller"); }
|
||||
CString getAuthorName() const override { return CString("Ozan Caglayan"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Galatasaray University"); }
|
||||
// + Stimulation support & some code refactoring in v1.1 by Jussi T. Lindgren / Inria
|
||||
CString getShortDescription() const override { return CString("Sends OSC messages to an OSC controller"); }
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return CString(
|
||||
"This box allows OpenViBE to send OSC (Open Sound Control) messages to an OSC server. See http://www.opensoundcontrol.org to learn about the OSC protocol and its use cases.");
|
||||
}
|
||||
|
||||
CString getCategory() const override { return CString("Acquisition and network IO"); }
|
||||
CString getVersion() const override { return CString("1.1"); }
|
||||
CString getStockItemName() const override { return CString("gtk-network"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_OSCController; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmOSCController; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Input",OV_TypeId_Signal);
|
||||
|
||||
prototype.addInputSupport(OV_TypeId_Signal);
|
||||
prototype.addInputSupport(OV_TypeId_StreamedMatrix);
|
||||
prototype.addInputSupport(OV_TypeId_Stimulations);
|
||||
|
||||
prototype.addFlag(Kernel::BoxFlag_CanModifyInput);
|
||||
|
||||
prototype.addSetting("OSC Server IP",OV_TypeId_String, "127.0.0.1");
|
||||
prototype.addSetting("OSC Server Port",OV_TypeId_Integer, "9001");
|
||||
prototype.addSetting("OSC Address",OV_TypeId_String, "/a/b/c");
|
||||
|
||||
prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_OSCControllerDesc)
|
||||
};
|
||||
} // namespace NetworkIO
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
#if defined TARGET_HAS_ThirdPartyLSL
|
||||
|
||||
#include "ovpCBoxLSLExportGipsa.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace NetworkIO {
|
||||
|
||||
|
||||
bool CBoxAlgorithmLSLExportGipsa::initialize()
|
||||
{
|
||||
m_inputChannel1.initialize(this);
|
||||
|
||||
m_streamName = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
m_streamType = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
|
||||
|
||||
m_outlet = nullptr;
|
||||
m_stims.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmLSLExportGipsa::uninitialize()
|
||||
{
|
||||
m_inputChannel1.uninitialize();
|
||||
m_stims.clear();
|
||||
delete m_outlet;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmLSLExportGipsa::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmLSLExportGipsa::process()
|
||||
{
|
||||
if (!m_inputChannel1.isWorking())
|
||||
{
|
||||
m_inputChannel1.waitForSignalHeader();
|
||||
|
||||
if (m_inputChannel1.isWorking())
|
||||
{
|
||||
try
|
||||
{
|
||||
//if it fails here then most likely you are using the wrong dll - e.x debug instead of release or vice-versa
|
||||
lsl::stream_info info(m_streamName.toASCIIString(), m_streamType.toASCIIString(), int(m_inputChannel1.getNChannels()) + 1,
|
||||
double(m_inputChannel1.getSamplingRate()), lsl::cf_float32);
|
||||
|
||||
lsl::xml_element channels = info.desc().append_child("channels");
|
||||
|
||||
for (size_t i = 0; i < m_inputChannel1.getNChannels(); ++i)
|
||||
{
|
||||
channels.append_child("channel")
|
||||
.append_child_value("label", m_inputChannel1.getChannelName(i))
|
||||
.append_child_value("type", "EEG")
|
||||
.append_child_value("unit", "microvolts");
|
||||
}
|
||||
|
||||
channels.append_child("channel")
|
||||
.append_child_value("label", "Stimulations")
|
||||
.append_child_value("type", "marker");
|
||||
|
||||
if (m_outlet != nullptr) { this->getLogManager() << Kernel::LogLevel_Error << "Possible double initialization!\n"; }
|
||||
|
||||
m_outlet = new lsl::stream_outlet(info); //here the length of the buffered signal can be specified
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Could not initialize LSL library: " << e.what() << "\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//stimulations
|
||||
for (size_t i = 0; i < m_inputChannel1.getNStimulationBuffers(); ++i)
|
||||
{
|
||||
uint64_t tStart, tEnd;
|
||||
IStimulationSet* set = m_inputChannel1.getStimulation(tStart, tEnd, i);
|
||||
|
||||
for (size_t j = 0; j < set->getStimulationCount(); ++j)
|
||||
{
|
||||
uint64_t time = m_inputChannel1.getStartTimestamp() + set->getStimulationDate(j);
|
||||
const uint64_t identifier = set->getStimulationIdentifier(j);
|
||||
|
||||
|
||||
if (m_stims.empty())
|
||||
{
|
||||
m_stims.push_back(std::pair<float, uint64_t>(float(identifier), time));
|
||||
//std::cout<< "added: " << m_stims[m_stims.size()-1].first << " " << m_stims[m_stims.size()-1].second<< "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto last = m_stims[m_stims.size() - 1];
|
||||
if (last.first != identifier && last.second != time)
|
||||
{
|
||||
m_stims.push_back(std::pair<float, uint64_t>(float(identifier), time));
|
||||
//std::cout<< "added: " << m_stims[m_stims.size()-1].first << " " << m_stims[m_stims.size()-1].second<< "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
//std::cout<< "duplicate: " << m_stims[m_stims.size()-1].first << " " << m_stims[m_stims.size()-1].second<< "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//signal
|
||||
for (size_t i = 0; i < m_inputChannel1.getNSignalBuffers(); ++i)
|
||||
{
|
||||
uint64_t tStart, tEnd;
|
||||
double* inputBuffer = m_inputChannel1.getSignal(tStart, tEnd, i);
|
||||
|
||||
if (inputBuffer)
|
||||
{
|
||||
const size_t samplesPerChannelInput = m_inputChannel1.getNSamples();
|
||||
std::vector<std::vector<float>> mychunk(samplesPerChannelInput);
|
||||
|
||||
for (size_t k = 0; k < samplesPerChannelInput; ++k) { mychunk[k] = std::vector<float>(m_inputChannel1.getNChannels() + 1); }
|
||||
|
||||
//Fill a matrix - OpenVibe provides the data ch1 (all values from all samples), ch2(all values from all samples) ... chN,
|
||||
//In the generated chunk every row is a single sample (containing the data from all channels) and every column number is the number of the channel
|
||||
for (size_t k = 0; k < m_inputChannel1.getNChannels(); ++k)
|
||||
{
|
||||
for (size_t j = 0; j < samplesPerChannelInput; ++j)
|
||||
{
|
||||
const size_t index = (k * samplesPerChannelInput) + j;
|
||||
mychunk[j][k] = float(inputBuffer[index]); // @note 64bit->32bit conversion
|
||||
}
|
||||
}
|
||||
|
||||
//Process stimulations and add them to the output in a dedicated channel
|
||||
std::vector<float> stimChan = std::vector<float>(samplesPerChannelInput);
|
||||
|
||||
auto it = m_stims.begin();
|
||||
while (it != m_stims.end())
|
||||
{
|
||||
auto current = *it;
|
||||
|
||||
if (!(current.second >= tStart && current.second <= tEnd))
|
||||
{
|
||||
// not in current time range, do not send now.
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
const uint64_t posCurrent = CTime(current.second).toSampleCount(m_inputChannel1.getSamplingRate());
|
||||
const uint64_t posStart = CTime(tStart).toSampleCount(m_inputChannel1.getSamplingRate());
|
||||
//uint64_t posEnd = CTime(tStart).toSampleCount(m_inputChannel1.getSamplingRate());
|
||||
|
||||
int pos = int(posCurrent) - int(posStart);
|
||||
if (pos < 0) { pos = 0; } //fix position
|
||||
if (pos == int(stimChan.size())) { pos = int(stimChan.size() - 1); } //fix position
|
||||
|
||||
if (pos >= 0 && pos < int(stimChan.size()))
|
||||
{
|
||||
stimChan[pos] = float(current.first);
|
||||
//std::cout<< "pos relative: " << pos << " value: " << stim_chan[pos] << " time:" << CTime(current.second).toSeconds()<< "\n";
|
||||
}
|
||||
else { this->getLogManager() << Kernel::LogLevel_Warning << "Bad stimulation position: " << pos << "stim code: " << current.first << "\n"; }
|
||||
|
||||
// processed, erase
|
||||
it = m_stims.erase(it);
|
||||
}
|
||||
|
||||
//add the stim channel at the end of the matrix
|
||||
const size_t k = m_inputChannel1.getNChannels();
|
||||
for (size_t j = 0; j < samplesPerChannelInput; ++j) { mychunk[j][k] = stimChan[j]; }
|
||||
|
||||
//send all channels
|
||||
m_outlet->push_chunk(mychunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
} // namespace NetworkIO
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyLSL
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <vector>
|
||||
|
||||
#include "../ovpCInputChannel.h"
|
||||
#include <lsl_cpp.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace NetworkIO {
|
||||
class CBoxAlgorithmLSLExportGipsa final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
CBoxAlgorithmLSLExportGipsa() : m_inputChannel1(0) {}
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_CBoxAlgorithmLSLExportGipsa)
|
||||
|
||||
protected:
|
||||
|
||||
int64_t m_decimationFactor = 0;
|
||||
uint64_t m_outputSampling = 0;
|
||||
CString m_streamName;
|
||||
CString m_streamType;
|
||||
|
||||
SignalProcessing::CInputChannel m_inputChannel1;
|
||||
|
||||
lsl::stream_outlet* m_outlet = nullptr;
|
||||
std::vector<std::pair<float, uint64_t>> m_stims;//identifier,time
|
||||
};
|
||||
|
||||
class CBoxAlgorithmLSLExportGipsaDesc final : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("LSL Export (Gipsa)"); }
|
||||
CString getAuthorName() const override { return CString("Anton Andreev"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Gipsa-lab"); }
|
||||
|
||||
CString getShortDescription() const override { return CString("Streams signal outside OpenVibe using Lab Streaming Layer library"); }
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return CString("More on how to read the signal in your application: https://code.google.com/p/labstreaminglayer/");
|
||||
}
|
||||
|
||||
CString getCategory() const override { return CString("Acquisition and network IO"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getStockItemName() const override { return CString("gtk-connect"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_CBoxAlgorithmLSLExportGipsa; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmLSLExportGipsa; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Input signal", OV_TypeId_Signal);
|
||||
prototype.addInput("Input stimulations", OV_TypeId_Stimulations);
|
||||
prototype.addSetting("Stream name", OV_TypeId_String, "OpenViBE Stream");
|
||||
prototype.addSetting("Stream type", OV_TypeId_String, "EEG");
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_CBoxAlgorithmLSLExportGipsaDesc)
|
||||
};
|
||||
} // namespace NetworkIO
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
#include "ovpCInputChannel.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CInputChannel::initialize(Toolkit::TBoxAlgorithm<IBoxAlgorithm>* boxAlgorithm)
|
||||
{
|
||||
m_isWorking = false;
|
||||
|
||||
m_startTimestamp = 0;
|
||||
m_endTimestamp = 0;
|
||||
|
||||
m_stimulationSet = nullptr;
|
||||
m_boxAlgorithm = boxAlgorithm;
|
||||
|
||||
m_signalDecoder = new Toolkit::TSignalDecoder<Toolkit::TBoxAlgorithm<IBoxAlgorithm>>();
|
||||
m_signalDecoder->initialize(*m_boxAlgorithm, 0);
|
||||
|
||||
m_stimDecoder = new Toolkit::TStimulationDecoder<Toolkit::TBoxAlgorithm<IBoxAlgorithm>>();
|
||||
m_stimDecoder->initialize(*m_boxAlgorithm, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CInputChannel::uninitialize() const
|
||||
{
|
||||
m_stimDecoder->uninitialize();
|
||||
delete m_stimDecoder;
|
||||
|
||||
m_signalDecoder->uninitialize();
|
||||
delete m_signalDecoder;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CInputChannel::waitForSignalHeader()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = m_boxAlgorithm->getDynamicBoxContext();
|
||||
|
||||
if (boxContext.getInputChunkCount(m_signalChannel))
|
||||
{
|
||||
m_signalDecoder->decode(0);
|
||||
|
||||
if (m_signalDecoder->isHeaderReceived())
|
||||
{
|
||||
m_isWorking = true;
|
||||
|
||||
m_startTimestamp = boxContext.getInputChunkStartTime(m_signalChannel, 0);
|
||||
m_endTimestamp = boxContext.getInputChunkEndTime(m_signalChannel, 0);
|
||||
|
||||
boxContext.markInputAsDeprecated(m_signalChannel, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
IStimulationSet* CInputChannel::getStimulation(uint64_t& startTime, uint64_t& endTime, const size_t index)
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = m_boxAlgorithm->getDynamicBoxContext();
|
||||
|
||||
m_stimDecoder->decode(index);
|
||||
m_stimulationSet = m_stimDecoder->getOutputStimulationSet();
|
||||
|
||||
startTime = boxContext.getInputChunkStartTime(m_stimulationChannel, index);
|
||||
endTime = boxContext.getInputChunkEndTime(m_stimulationChannel, index);
|
||||
|
||||
boxContext.markInputAsDeprecated(m_stimulationChannel, index);
|
||||
|
||||
return m_stimulationSet;
|
||||
}
|
||||
|
||||
IStimulationSet* CInputChannel::discardStimulation(const size_t index)
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = m_boxAlgorithm->getDynamicBoxContext();
|
||||
|
||||
m_stimDecoder->decode(index);
|
||||
m_stimulationSet = m_stimDecoder->getOutputStimulationSet();
|
||||
|
||||
boxContext.markInputAsDeprecated(m_stimulationChannel, index);
|
||||
|
||||
return m_stimulationSet;
|
||||
}
|
||||
|
||||
|
||||
double* CInputChannel::getSignal(uint64_t& startTime, uint64_t& endTime, const size_t index) const
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = m_boxAlgorithm->getDynamicBoxContext();
|
||||
m_signalDecoder->decode(index);
|
||||
if (!m_signalDecoder->isBufferReceived()) { return nullptr; }
|
||||
|
||||
startTime = boxContext.getInputChunkStartTime(m_signalChannel, index);
|
||||
endTime = boxContext.getInputChunkEndTime(m_signalChannel, index);
|
||||
|
||||
boxContext.markInputAsDeprecated(m_signalChannel, index);
|
||||
|
||||
return m_signalDecoder->getOutputMatrix()->getBuffer();
|
||||
}
|
||||
|
||||
|
||||
double* CInputChannel::discardSignal(const size_t index) const
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = m_boxAlgorithm->getDynamicBoxContext();
|
||||
m_signalDecoder->decode(index);
|
||||
if (!m_signalDecoder->isBufferReceived()) { return nullptr; }
|
||||
|
||||
boxContext.markInputAsDeprecated(m_signalChannel, index);
|
||||
|
||||
return m_signalDecoder->getOutputMatrix()->getBuffer();
|
||||
}
|
||||
|
||||
#if 0
|
||||
void CInputChannel::copyData(const bool copyFirstBlock, size_t index)
|
||||
{
|
||||
CMatrix*& matrixBuffer = m_oMatrixBuffer[index & 1];
|
||||
|
||||
double* srcData = m_signalDecoder->getOutputMatrix()->getBuffer() + (copyFirstBlock ? 0 : m_firstBlock);
|
||||
double* dstData = matrixBuffer->getBuffer() + (copyFirstBlock ? m_secondBlock : 0);
|
||||
size_t size = (copyFirstBlock ? m_firstBlock : m_secondBlock)*sizeof(double);
|
||||
|
||||
for (size_t i=0; i < m_nChannels; i++, srcData += m_nSamples, dstData += m_nSamples) { System::Memory::copy(dstData, srcData, size); }
|
||||
}
|
||||
#endif
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
// @author Gipsa-lab
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
/**
|
||||
Use this class to receive send and stimulations channels
|
||||
*/
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
class CInputChannel
|
||||
{
|
||||
typedef enum
|
||||
{
|
||||
SIGNAL_CHANNEL,
|
||||
STIMULATION_CHANNEL,
|
||||
NB_CHANNELS,
|
||||
} channel_t;
|
||||
|
||||
public:
|
||||
|
||||
explicit CInputChannel(const uint16_t index = 0)
|
||||
: m_signalChannel(index * NB_CHANNELS + SIGNAL_CHANNEL), m_stimulationChannel(index * NB_CHANNELS + STIMULATION_CHANNEL) {}
|
||||
|
||||
~CInputChannel() { }
|
||||
|
||||
bool initialize(Toolkit::TBoxAlgorithm<IBoxAlgorithm>* boxAlgorithm);
|
||||
bool uninitialize() const;
|
||||
|
||||
bool isLastChannel(const size_t index) const { return index == m_stimulationChannel; }
|
||||
bool isWorking() const { return m_isWorking; }
|
||||
bool waitForSignalHeader();
|
||||
size_t getNStimulationBuffers() const { return m_boxAlgorithm->getDynamicBoxContext().getInputChunkCount(m_stimulationChannel); }
|
||||
size_t getNSignalBuffers() const { return m_boxAlgorithm->getDynamicBoxContext().getInputChunkCount(m_signalChannel); }
|
||||
IStimulationSet* getStimulation(uint64_t& startTime, uint64_t& endTime, size_t index);
|
||||
IStimulationSet* discardStimulation(size_t index);
|
||||
double* getSignal(uint64_t& startTime, uint64_t& endTime, size_t index) const;
|
||||
double* discardSignal(size_t index) const;
|
||||
uint64_t getSamplingRate() const { return m_signalDecoder->getOutputSamplingRate(); }
|
||||
size_t getNChannels() const { return m_signalDecoder->getOutputMatrix()->getDimensionSize(0); }
|
||||
size_t getNSamples() const { return m_signalDecoder->getOutputMatrix()->getDimensionSize(1); }
|
||||
uint64_t getStartTimestamp() const { return m_startTimestamp; }
|
||||
uint64_t getEndTimestamp() const { return m_endTimestamp; }
|
||||
const char* getChannelName(const size_t index) const { return m_signalDecoder->getOutputMatrix()->getDimensionLabel(0, index); }
|
||||
const Kernel::TParameterHandler<CMatrix*>& getOpMatrix() const { return m_signalDecoder->getOutputMatrix(); }
|
||||
|
||||
protected:
|
||||
size_t m_signalChannel = 0;
|
||||
size_t m_stimulationChannel = 0;
|
||||
bool m_isWorking = false;
|
||||
|
||||
uint64_t m_startTimestamp = 0;
|
||||
uint64_t m_endTimestamp = 0;
|
||||
|
||||
IStimulationSet* m_stimulationSet = nullptr;
|
||||
|
||||
// parent memory
|
||||
Toolkit::TBoxAlgorithm<IBoxAlgorithm>* m_boxAlgorithm = nullptr;
|
||||
|
||||
// signal section
|
||||
//Kernel::IAlgorithmProxy* m_signalDecoder;
|
||||
Toolkit::TSignalDecoder<Toolkit::TBoxAlgorithm<IBoxAlgorithm>>* m_signalDecoder = nullptr;
|
||||
|
||||
// stimulation section
|
||||
Toolkit::TStimulationDecoder<Toolkit::TBoxAlgorithm<IBoxAlgorithm>>* m_stimDecoder = nullptr;
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
// Boxes
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#define OVP_ClassId_BoxAlgorithm_OSCController OpenViBE::CIdentifier(0xC66F2F0C, 0x3BA5B424)
|
||||
#define OVP_ClassId_BoxAlgorithm_OSCControllerDesc OpenViBE::CIdentifier(0xF7A35BD7, 0x6331C7D9)
|
||||
#define OVP_ClassId_BoxAlgorithm_CBoxAlgorithmLSLExportGipsa OpenViBE::CIdentifier(0x591D2E94, 0x221C23AD)
|
||||
#define OVP_ClassId_BoxAlgorithm_CBoxAlgorithmLSLExportGipsaDesc OpenViBE::CIdentifier(0x22AF11F5, 0x58F2787D)
|
||||
|
||||
// Global defines
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#ifdef TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
#include "ovp_global_defines.h"
|
||||
#endif // TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
|
||||
#define OV_AttributeId_Box_FlagIsUnstable OpenViBE::CIdentifier(0x666FFFFF, 0x666FFFFF)
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#include <vector>
|
||||
#include <openvibe/ov_all.h>
|
||||
#include "ovp_defines.h"
|
||||
|
||||
#include "box-algorithms/osc-controller/ovpCBoxAlgorithmOSCController.h"
|
||||
|
||||
// @BEGIN gipsa
|
||||
|
||||
#include "box-algorithms/ovpCBoxLSLExportGipsa.h"
|
||||
|
||||
// @END gipsa
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace NetworkIO {
|
||||
|
||||
OVP_Declare_Begin()
|
||||
OVP_Declare_New(CBoxAlgorithmOSCControllerDesc);
|
||||
context.getTypeManager().registerEnumerationEntry(OV_TypeId_BoxAlgorithmFlag, OV_AttributeId_Box_FlagIsUnstable.toString(),
|
||||
OV_AttributeId_Box_FlagIsUnstable.id());
|
||||
// @BEGIN gipsa
|
||||
#if defined TARGET_HAS_ThirdPartyLSL
|
||||
OVP_Declare_New(CBoxAlgorithmLSLExportGipsaDesc);
|
||||
#endif // TARGET_HAS_ThirdPartyLSL
|
||||
// @END gipsa
|
||||
|
||||
OVP_Declare_End()
|
||||
|
||||
} // namespace NetworkIO
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,36 @@
|
||||
PROJECT(openvibe-plugins-contrib-python2)
|
||||
|
||||
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
|
||||
SET(PROJECT_VERSION ${OV_GLOBAL_VERSION_STRING})
|
||||
|
||||
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.inl)
|
||||
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
|
||||
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
FOLDER ${PLUGINS_FOLDER}
|
||||
COMPILE_FLAGS "-DOVP_Exports -DOVP_Shared")
|
||||
|
||||
# -----------------------------
|
||||
INCLUDE("FindOpenViBE")
|
||||
INCLUDE("FindOpenViBECommon")
|
||||
INCLUDE("FindOpenViBEToolkit")
|
||||
INCLUDE("FindOpenViBEModuleEBML")
|
||||
INCLUDE("FindThirdPartyBoost")
|
||||
INCLUDE("FindThirdPartyPython2")
|
||||
# -----------------------------
|
||||
# Install files
|
||||
# -----------------------------
|
||||
INSTALL(TARGETS ${PROJECT_NAME}
|
||||
RUNTIME DESTINATION ${DIST_BINDIR}
|
||||
LIBRARY DESTINATION ${DIST_LIBDIR}
|
||||
ARCHIVE DESTINATION ${DIST_LIBDIR})
|
||||
|
||||
IF(WIN32)
|
||||
# The pygame scenario doesn't work on Windows, so do not install it
|
||||
INSTALL(DIRECTORY box-tutorials DESTINATION ${DIST_DATADIR}/openvibe/scenarios/ PATTERN "*-pygame-*" EXCLUDE)
|
||||
ELSE()
|
||||
INSTALL(DIRECTORY box-tutorials DESTINATION ${DIST_DATADIR}/openvibe/scenarios/)
|
||||
ENDIF()
|
||||
|
||||
INSTALL(DIRECTORY share/ DESTINATION ${DIST_DATADIR}/openvibe/plugins/python2)
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
<OpenViBE-Scenario>
|
||||
<FormatVersion>1</FormatVersion>
|
||||
<Creator>openvibe</Creator>
|
||||
<CreatorVersion>2.0</CreatorVersion>
|
||||
<Boxes>
|
||||
<Box>
|
||||
<Identifier>(0x00003954, 0x000010bb)</Identifier>
|
||||
<Name>Hello World!</Name>
|
||||
<AlgorithmClassIdentifier>(0x5dc4f669, 0xd3fd4d64)</AlgorithmClassIdentifier>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Clock frequency (Hz)</Name>
|
||||
<DefaultValue>60</DefaultValue>
|
||||
<Value>1</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0xb0d0db45, 0x49cbc34a)</TypeIdentifier>
|
||||
<Name>Script</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value>${Player_ScenarioDirectory}/scripts/python-hello-world.py</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
|
||||
<Name>Message</Name>
|
||||
<DefaultValue>Hello World!</DefaultValue>
|
||||
<Value>Hello World!</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x17ee7c08, 0x94c14893)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>304</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa963f5, 0x1a638cd4)</Identifier>
|
||||
<Value>51</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>176</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x30a4e5c9, 0x83502953)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x2a651510, 0xb4fad0d4)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x61d11811, 0x71e65362)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xad100179, 0xa3c984ab)</Identifier>
|
||||
<Value>101</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc67a01dc, 0x28ce06c1)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf191c1c8, 0xa0123976)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
</Boxes>
|
||||
<Links></Links>
|
||||
<Comments>
|
||||
<Comment>
|
||||
<Identifier>(0x000047c8, 0x000050cf)</Identifier>
|
||||
<Text><b>Hello World!</b>
|
||||
|
||||
Prints a friendly message.
|
||||
|
||||
You can customize the message by editing
|
||||
the box configuration.
|
||||
</Text>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x473d9a43, 0x97fc0a97)</Identifier>
|
||||
<Value>-80</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x7234b86b, 0x2b8651a5)</Identifier>
|
||||
<Value>304</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Comment>
|
||||
</Comments>
|
||||
<Metadata>
|
||||
<Entry>
|
||||
<Identifier>(0x0000775c, 0x000078ff)</Identifier>
|
||||
<Type>(0x3bcce5d2, 0x43f2d968)</Type>
|
||||
<Data>[{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"height":507,"identifier":"(0x00003962, 0x00000a21)","index":0,"name":"Default window","parentIdentifier":"(0xffffffff, 0xffffffff)","type":1,"width":798},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"identifier":"(0x0000781e, 0x00004e65)","index":0,"name":"Default tab","parentIdentifier":"(0x00003962, 0x00000a21)","type":2},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":0,"identifier":"(0x00006808, 0x00000e28)","index":0,"name":"Empty","parentIdentifier":"(0x0000781e, 0x00004e65)","type":0}]</Data>
|
||||
</Entry>
|
||||
</Metadata>
|
||||
</OpenViBE-Scenario>
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
<OpenViBE-Scenario>
|
||||
<FormatVersion>1</FormatVersion>
|
||||
<Creator>openvibe</Creator>
|
||||
<CreatorVersion>2.0</CreatorVersion>
|
||||
<Boxes>
|
||||
<Box>
|
||||
<Identifier>(0x00004db4, 0x00006a35)</Identifier>
|
||||
<Name>Python 2 scripting</Name>
|
||||
<AlgorithmClassIdentifier>(0x5dc4f669, 0xd3fd4d64)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>New input</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Clock frequency (Hz)</Name>
|
||||
<DefaultValue>64</DefaultValue>
|
||||
<Value>64</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0xb0d0db45, 0x49cbc34a)</TypeIdentifier>
|
||||
<Name>Script</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value>${Player_ScenarioDirectory}/scripts/python-print-stimulations.py</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x17ee7c08, 0x94c14893)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>128.000000</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa963f5, 0x1a638cd4)</Identifier>
|
||||
<Value>33</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>400.000000</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x30a4e5c9, 0x83502953)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x2a651510, 0xb4fad0d4)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x61d11811, 0x71e65362)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xad100179, 0xa3c984ab)</Identifier>
|
||||
<Value>114</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
|
||||
<Value>(0x00000000, 0x05874423)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
|
||||
<Value>false</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf191c1c8, 0xa0123976)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00005777, 0x00006c19)</Identifier>
|
||||
<Name>Clock stimulator</Name>
|
||||
<AlgorithmClassIdentifier>(0x4f756d3f, 0x29ff0b96)</AlgorithmClassIdentifier>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Generated stimulations</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Interstimulation interval (in sec)</Name>
|
||||
<DefaultValue>1.0</DefaultValue>
|
||||
<Value>1.0</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
|
||||
<Name>Stimulation</Name>
|
||||
<DefaultValue>OVTK_StimulationId_Label_00</DefaultValue>
|
||||
<Value>OVTK_StimulationId_Label_00</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>48.000000</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa963f5, 0x1a638cd4)</Identifier>
|
||||
<Value>23</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>400.000000</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x27b3ee3c, 0xc50527e6)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xad100179, 0xa3c984ab)</Identifier>
|
||||
<Value>114</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
|
||||
<Value>(0x00000000, 0x0062599e)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
|
||||
<Value>false</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
</Boxes>
|
||||
<Links>
|
||||
<Link>
|
||||
<Identifier>(0x0000412b, 0x00003ac4)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00005777, 0x00006c19)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00004db4, 0x00006a35)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1b32c44c, 0x1905e0e9)</Identifier>
|
||||
<Value>66</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x358ae8b5, 0x0f8bacd1)</Identifier>
|
||||
<Value>400</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x3f0a3b27, 0x570913d2)</Identifier>
|
||||
<Value>107</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x6267b5c5, 0x676e3e42)</Identifier>
|
||||
<Value>400</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Link>
|
||||
</Links>
|
||||
<Comments>
|
||||
<Comment>
|
||||
<Identifier>(0x00003cb4, 0x00004c5e)</Identifier>
|
||||
<Text>The clock stimulator generates
|
||||
1 stim per second.
|
||||
|
||||
The associated python script should
|
||||
print all the received stimulations.</Text>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x473d9a43, 0x97fc0a97)</Identifier>
|
||||
<Value>640.000000</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x7234b86b, 0x2b8651a5)</Identifier>
|
||||
<Value>96.000000</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Comment>
|
||||
</Comments>
|
||||
<Metadata>
|
||||
<Entry>
|
||||
<Identifier>(0x0000775c, 0x000078ff)</Identifier>
|
||||
<Type>(0x3bcce5d2, 0x43f2d968)</Type>
|
||||
<Data>[{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"height":1,"identifier":"(0x000051c6, 0x0000403f)","index":0,"name":"Default window","parentIdentifier":"(0xffffffff, 0xffffffff)","type":1,"width":1},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"identifier":"(0x00006099, 0x00001a73)","index":0,"name":"Default tab","parentIdentifier":"(0x000051c6, 0x0000403f)","type":2},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":0,"identifier":"(0x00004197, 0x00001dca)","index":0,"name":"Empty","parentIdentifier":"(0x00006099, 0x00001a73)","type":0}]</Data>
|
||||
</Entry>
|
||||
</Metadata>
|
||||
</OpenViBE-Scenario>
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
<OpenViBE-Scenario>
|
||||
<FormatVersion>1</FormatVersion>
|
||||
<Creator>OpenVIBE</Creator>
|
||||
<CreatorVersion>0.2.99</CreatorVersion>
|
||||
<Settings></Settings>
|
||||
<Inputs></Inputs>
|
||||
<Outputs></Outputs>
|
||||
<Boxes>
|
||||
<Box>
|
||||
<Identifier>(0x441e2100, 0x610e711c)</Identifier>
|
||||
<Name>Multiprocessing</Name>
|
||||
<AlgorithmClassIdentifier>(0x5dc4f669, 0xd3fd4d64)</AlgorithmClassIdentifier>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Clock frequency (Hz)</Name>
|
||||
<DefaultValue>60</DefaultValue>
|
||||
<Value>4</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0xb0d0db45, 0x49cbc34a)</TypeIdentifier>
|
||||
<Name>Script</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value>${Player_ScenarioDirectory}/scripts/python-multiprocessing.py</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x17ee7c08, 0x94c14893)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>224</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>256</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x30a4e5c9, 0x83502953)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x2a651510, 0xb4fad0d4)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x61d11811, 0x71e65362)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf191c1c8, 0xa0123976)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x47008917, 0x4f6a4317)</Identifier>
|
||||
<Name>Moving circle</Name>
|
||||
<AlgorithmClassIdentifier>(0x5dc4f669, 0xd3fd4d64)</AlgorithmClassIdentifier>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Clock frequency (Hz)</Name>
|
||||
<DefaultValue>60</DefaultValue>
|
||||
<Value>4</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0xb0d0db45, 0x49cbc34a)</TypeIdentifier>
|
||||
<Name>Script</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value>${Player_ScenarioDirectory}/scripts/python-moving-circle.py</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x17ee7c08, 0x94c14893)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>352</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>256</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x30a4e5c9, 0x83502953)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x2a651510, 0xb4fad0d4)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x61d11811, 0x71e65362)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf191c1c8, 0xa0123976)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x51f53da2, 0x789994d2)</Identifier>
|
||||
<Name>Star field</Name>
|
||||
<AlgorithmClassIdentifier>(0x5dc4f669, 0xd3fd4d64)</AlgorithmClassIdentifier>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Clock frequency (Hz)</Name>
|
||||
<DefaultValue>60</DefaultValue>
|
||||
<Value>4</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0xb0d0db45, 0x49cbc34a)</TypeIdentifier>
|
||||
<Name>Script</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value>${Player_ScenarioDirectory}/scripts/python-star-field.py</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x17ee7c08, 0x94c14893)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>416</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>256</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x30a4e5c9, 0x83502953)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x2a651510, 0xb4fad0d4)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x61d11811, 0x71e65362)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf191c1c8, 0xa0123976)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
</Boxes>
|
||||
<Links></Links>
|
||||
<Comments>
|
||||
<Comment>
|
||||
<Identifier>(0x0ad104c8, 0x2ee0799b)</Identifier>
|
||||
<Text><big>Do not forget to look at the python scripts used
|
||||
in all of the python boxes in this scenario.</big></Text>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x473d9a43, 0x97fc0a97)</Identifier>
|
||||
<Value>368</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x7234b86b, 0x2b8651a5)</Identifier>
|
||||
<Value>-32</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Comment>
|
||||
<Comment>
|
||||
<Identifier>(0x4e7de8aa, 0x561bc220)</Identifier>
|
||||
<Text><b>Multiprocessing</b>
|
||||
|
||||
Creates a new processus
|
||||
which works in the background.</Text>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x473d9a43, 0x97fc0a97)</Identifier>
|
||||
<Value>480</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x7234b86b, 0x2b8651a5)</Identifier>
|
||||
<Value>224</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Comment>
|
||||
<Comment>
|
||||
<Identifier>(0x6bfbf8ad, 0x3b970ba1)</Identifier>
|
||||
<Text><b>Moving Circle</b> and <b>Starfield</b>
|
||||
|
||||
These two scripts illustrate running
|
||||
several instances of pygame at
|
||||
the same time.</Text>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x473d9a43, 0x97fc0a97)</Identifier>
|
||||
<Value>496</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x7234b86b, 0x2b8651a5)</Identifier>
|
||||
<Value>384</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Comment>
|
||||
<Comment>
|
||||
<Identifier>(0x704b39c7, 0x74448e12)</Identifier>
|
||||
<Text><big><b><span color="red">Warning</span></b></big>
|
||||
|
||||
For the time being multiprocessing and pygame only work
|
||||
on <b>Linux</b> operating system provided <span color="red">you have installed <i>pygame</i> module</span> in Python.</Text>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x473d9a43, 0x97fc0a97)</Identifier>
|
||||
<Value>368</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x7234b86b, 0x2b8651a5)</Identifier>
|
||||
<Value>80</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Comment>
|
||||
</Comments>
|
||||
<Metadata>
|
||||
<Entry>
|
||||
<Identifier>(0x0000775c, 0x000078ff)</Identifier>
|
||||
<Type>(0x3bcce5d2, 0x43f2d968)</Type>
|
||||
<Data>[{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"height":1,"identifier":"(0x71777b71, 0x7d9b955c)","name":"Default window","parentIdentifier":"(0xffffffff, 0xffffffff)","type":1,"width":1},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"identifier":"(0x3587c6af, 0x788f3c37)","index":0,"name":"Default tab","parentIdentifier":"(0x71777b71, 0x7d9b955c)","type":2},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":0,"identifier":"(0x5aab9723, 0x14698840)","index":0,"name":"Empty","parentIdentifier":"(0x3587c6af, 0x788f3c37)","type":0}]</Data>
|
||||
</Entry>
|
||||
</Metadata>
|
||||
</OpenViBE-Scenario>
|
||||
+1183
File diff suppressed because it is too large
Load Diff
+31
@@ -0,0 +1,31 @@
|
||||
# We construct a box instance that inherits from the basic OVBox class
|
||||
class MyOVBox(OVBox):
|
||||
# the constructor creates the box and initializes object variables
|
||||
def __init__(self):
|
||||
OVBox.__init__(self)
|
||||
self.stimLabel = None
|
||||
self.stimCode = None
|
||||
|
||||
# the initialize method reads settings and outputs the first header
|
||||
def initialize(self):
|
||||
# the stim label is taken from the box setting
|
||||
self.stimLabel = self.setting['Stimulation']
|
||||
# we get the corresponding code using the OpenViBE_stimulation dictionnary
|
||||
self.stimCode = OpenViBE_stimulation[self.stimLabel]
|
||||
# we append to the box output a stimulation header. This is just a header, dates are 0.
|
||||
self.output[0].append(OVStimulationHeader(0., 0.))
|
||||
|
||||
def process(self):
|
||||
# During each process call we produce a stimulation
|
||||
# A stimulation set is a chunk which starts at current time and end time is the time step between two calls
|
||||
stimSet = OVStimulationSet(self.getCurrentTime(), self.getCurrentTime()+1./self.getClock())
|
||||
# the date of the stimulation is simply the current openvibe time when calling the box process
|
||||
stimSet.append(OVStimulation(self.stimCode, self.getCurrentTime(), 0.))
|
||||
self.output[0].append(stimSet)
|
||||
|
||||
def uninitialize(self):
|
||||
# we send a stream end.
|
||||
end = self.getCurrentTime()
|
||||
self.output[0].append(OVStimulationEnd(end, end))
|
||||
|
||||
box = MyOVBox()
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
|
||||
class MyOVBox(OVBox):
|
||||
def __init__(self):
|
||||
OVBox.__init__(self)
|
||||
|
||||
def initialize(self):
|
||||
# nop
|
||||
return
|
||||
|
||||
def process(self):
|
||||
# print the string specified in the box configuration.
|
||||
# 'Message' is the name of the config entry.
|
||||
print(self.setting['Message'])
|
||||
|
||||
def uninitialize(self):
|
||||
# nop
|
||||
return
|
||||
|
||||
box = MyOVBox()
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
from multiprocessing import Process, Event
|
||||
import time
|
||||
import pygame
|
||||
from pygame.locals import *
|
||||
import math
|
||||
from random import randint, randrange
|
||||
import sys
|
||||
sys.argv = ['openvibe']
|
||||
|
||||
class OVProcess(Process):
|
||||
def __init__(self):
|
||||
Process.__init__(self)
|
||||
self.initialized = Event()
|
||||
self.stop_asked = Event()
|
||||
|
||||
def initialize(self):
|
||||
pass
|
||||
|
||||
def process(self):
|
||||
pass
|
||||
|
||||
def uninitialize(self):
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
self.stop_asked.set()
|
||||
|
||||
def run(self):
|
||||
self.initialize()
|
||||
self.initialized.set()
|
||||
while not self.stop_asked.is_set():
|
||||
self.process()
|
||||
self.uninitialize()
|
||||
|
||||
class MovingCircle(OVProcess):
|
||||
def __init__(self):
|
||||
OVProcess.__init__(self)
|
||||
self.screen = None
|
||||
self.pos = [320,240]
|
||||
self.speed = 5
|
||||
self.dirx = -self.speed
|
||||
self.diry = -self.speed
|
||||
|
||||
def initialize(self):
|
||||
pygame.init()
|
||||
pygame.display.set_caption(self.name)
|
||||
self.screen = pygame.display.set_mode((640,480),0,32)
|
||||
|
||||
def process(self):
|
||||
begining = time.time()
|
||||
self.pos[0] += self.dirx
|
||||
self.pos[1] += self.diry
|
||||
if self.pos[0] > 640 or self.pos[0] < 0:
|
||||
self.dirx = -self.dirx
|
||||
if self.pos[1] > 480 or self.pos[1] < 0:
|
||||
self.diry = -self.diry
|
||||
self.screen.fill((255,255,255))
|
||||
pygame.draw.circle(self.screen, (0,0,0), self.pos, 25, 0)
|
||||
pygame.display.update()
|
||||
while (time.time() - begining) < (1.0/60.0):
|
||||
pass
|
||||
|
||||
|
||||
class MyOVBox(OVBox):
|
||||
def __init__(self):
|
||||
OVBox.__init__(self)
|
||||
self.p = None
|
||||
def initialize(self):
|
||||
self.p = MovingCircle()
|
||||
self.p.start()
|
||||
|
||||
def process(self):
|
||||
pass
|
||||
|
||||
def uninitialize(self):
|
||||
self.p.stop()
|
||||
self.p.join()
|
||||
|
||||
if __name__ == '__main__':
|
||||
box = MyOVBox()
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
from multiprocessing import Process, Queue
|
||||
import time
|
||||
|
||||
class MyOVBox(OVBox):
|
||||
def __init__(self):
|
||||
OVBox.__init__(self)
|
||||
self.p = None
|
||||
self.q = None
|
||||
|
||||
def f(self, queue):
|
||||
while True:
|
||||
queue.put('hello')
|
||||
time.sleep(1)
|
||||
|
||||
def initialize(self):
|
||||
print "process initialize!"
|
||||
self.q = Queue()
|
||||
self.p = Process(target=self.f, args=(self.q,))
|
||||
self.p.start()
|
||||
|
||||
def process(self):
|
||||
for i in range(self.q.qsize()):
|
||||
print self.q.get()
|
||||
|
||||
def uninitialize(self):
|
||||
print "process uninitialize!"
|
||||
self.p.terminate()
|
||||
self.p.join()
|
||||
|
||||
if __name__ == '__main__':
|
||||
box = MyOVBox()
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
|
||||
class MyOVBox(OVBox):
|
||||
def __init__(self):
|
||||
OVBox.__init__(self)
|
||||
|
||||
def initialize(self):
|
||||
# nop
|
||||
return
|
||||
|
||||
def process(self):
|
||||
for chunkIdx in range( len(self.input[0]) ):
|
||||
chunk = self.input[0].pop()
|
||||
if(type(chunk) == OVStimulationSet):
|
||||
for stimIdx in range(len(chunk)):
|
||||
stim=chunk.pop();
|
||||
print 'Received stim', stim.identifier, 'stamped at', stim.date, 's'
|
||||
#else:
|
||||
# print 'Received chunk of type ', type(chunk), " looking for StimulationSet"
|
||||
return
|
||||
|
||||
def uninitialize(self):
|
||||
# nop
|
||||
return
|
||||
|
||||
box = MyOVBox()
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import numpy
|
||||
|
||||
class MyOVBox(OVBox):
|
||||
def __init__(self):
|
||||
OVBox.__init__(self)
|
||||
self.signalHeader = None
|
||||
|
||||
def process(self):
|
||||
for chunkIdx in range( len(self.input[0]) ):
|
||||
if(type(self.input[0][chunkIdx]) == OVSignalHeader):
|
||||
self.signalHeader = self.input[0].pop()
|
||||
|
||||
outputHeader = OVSignalHeader(self.signalHeader.startTime, self.signalHeader.endTime, [1, self.signalHeader.dimensionSizes[1]], ['Mean']+self.signalHeader.dimensionSizes[1]*[''], self.signalHeader.samplingRate)
|
||||
|
||||
self.output[0].append(outputHeader)
|
||||
|
||||
elif(type(self.input[0][chunkIdx]) == OVSignalBuffer):
|
||||
chunk = self.input[0].pop()
|
||||
numpyBuffer = numpy.array(chunk).reshape(tuple(self.signalHeader.dimensionSizes))
|
||||
numpyBuffer = numpyBuffer.mean(axis=0)
|
||||
chunk = OVSignalBuffer(chunk.startTime, chunk.endTime, numpyBuffer.tolist())
|
||||
self.output[0].append(chunk)
|
||||
|
||||
elif(type(self.input[0][chunkIdx]) == OVSignalEnd):
|
||||
self.output[0].append(self.input[0].pop())
|
||||
|
||||
box = MyOVBox()
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
from math import *
|
||||
import random, time
|
||||
|
||||
class MyOVBox(OVBox):
|
||||
def __init__(self):
|
||||
OVBox.__init__(self)
|
||||
self.nChannel = 0
|
||||
self.sampling = 0
|
||||
self.epochSampleCount = 0
|
||||
self.startTime = 0
|
||||
self.endTime = 0
|
||||
self.dimensionSizes = list()
|
||||
self.dimensionLabels = list()
|
||||
self.timeBuffer = list()
|
||||
self.signalBuffer = list()
|
||||
self.signalHeader = None
|
||||
|
||||
def initialize(self):
|
||||
random.seed(time.time())
|
||||
self.nChannel = int(self.setting['Channel count'])
|
||||
self.sampling = int(self.setting['Sampling frequency'])
|
||||
self.epochSampleCount = int(self.setting['Generated epoch sample count'])
|
||||
|
||||
#creation of the signal header
|
||||
for i in range(self.nChannel):
|
||||
self.dimensionLabels.append( 'Sinus'+str(i) )
|
||||
self.dimensionLabels += self.epochSampleCount*['']
|
||||
self.dimensionSizes = [self.nChannel, self.epochSampleCount]
|
||||
self.signalHeader = OVSignalHeader(0., 0., self.dimensionSizes, self.dimensionLabels, self.sampling)
|
||||
self.output[0].append(self.signalHeader)
|
||||
|
||||
#creation of the first signal chunk
|
||||
self.endTime = self.epochSampleCount
|
||||
self.signalBuffer = [0.]*self.nChannel*self.epochSampleCount
|
||||
self.update_timeBuffer()
|
||||
self.update_signalBuffer()
|
||||
|
||||
|
||||
def updateStartTime(self):
|
||||
self.startTime += self.epochSampleCount
|
||||
|
||||
def updateEndTime(self):
|
||||
self.endTime += self.epochSampleCount
|
||||
|
||||
def updateTimeBuffer(self):
|
||||
self.timeBuffer = range(self.startTime, self.endTime)
|
||||
for instant_index, instant in enumerate(self.timeBuffer):
|
||||
self.timeBuffer[instant_index] = float(instant)/self.sampling
|
||||
|
||||
|
||||
def updateSignalBuffer(self):
|
||||
for channel in range(self.nChannel):
|
||||
for instantIndex, instant in enumerate(self.timeBuffer):
|
||||
newSample = 100.*sin(2.*pi*float(channel+1)*instant) + (1000.*random.random()-500.)
|
||||
self.signalBuffer[channel*self.epochSampleCount+instantIndex] = newSample
|
||||
|
||||
def sendSignalBufferToOpenvibe(self):
|
||||
start = self.timeBuffer[0]
|
||||
end = self.timeBuffer[-1] + 1.0/self.sampling
|
||||
self.output[0].append( OVSignalBuffer(start, end, self.signalBuffer) )
|
||||
|
||||
def process(self):
|
||||
start = self.timeBuffer[0]
|
||||
end = self.timeBuffer[-1]
|
||||
if self.getCurrentTime() >= end:
|
||||
self.sendSignalBufferToOpenvibe()
|
||||
self.updateStartTime()
|
||||
self.updateEndTime()
|
||||
self.updateTimeBuffer()
|
||||
self.updateSignalBuffer()
|
||||
|
||||
def uninitialize(self):
|
||||
end = self.timeBuffer[-1]
|
||||
self.output[0].append(OVSignalEnd(end, end))
|
||||
|
||||
box = MyOVBox()
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
import numpy
|
||||
|
||||
class MyOVBox(OVBox):
|
||||
def __init__(self):
|
||||
OVBox.__init__(self)
|
||||
self.nChannel = 0
|
||||
self.sampling = 0
|
||||
self.epochSampleCount = 0
|
||||
self.startTime = 0.
|
||||
self.endTime = 0.
|
||||
self.dimensionSizes = list()
|
||||
self.dimensionLabels = list()
|
||||
self.timeBuffer = list()
|
||||
self.signalBuffer = None
|
||||
self.signalHeader = None
|
||||
|
||||
def initialize(self):
|
||||
self.nChannel = int(self.setting['Channel count'])
|
||||
self.sampling = int(self.setting['Sampling frequency'])
|
||||
self.epochSampleCount = int(self.setting['Generated epoch sample count'])
|
||||
|
||||
#creation of the signal header
|
||||
for i in range(self.nChannel):
|
||||
self.dimensionLabels.append( 'Sinus'+str(i) )
|
||||
self.dimensionLabels += self.epochSampleCount*['']
|
||||
self.dimensionSizes = [self.nChannel, self.epochSampleCount]
|
||||
self.signalHeader = OVSignalHeader(0., 0., self.dimensionSizes, self.dimensionLabels, self.sampling)
|
||||
self.output[0].append(self.signalHeader)
|
||||
|
||||
#creation of the first signal chunk
|
||||
self.endTime = 1.*self.epochSampleCount/self.sampling
|
||||
self.signalBuffer = numpy.zeros((self.nChannel, self.epochSampleCount))
|
||||
self.updateTimeBuffer()
|
||||
self.updateSignalBuffer()
|
||||
|
||||
def updateStartTime(self):
|
||||
self.startTime += 1.*self.epochSampleCount/self.sampling
|
||||
|
||||
def updateEndTime(self):
|
||||
self.endTime = float(self.startTime + 1.*self.epochSampleCount/self.sampling)
|
||||
|
||||
def updateTimeBuffer(self):
|
||||
self.timeBuffer = numpy.arange(self.startTime, self.endTime, 1./self.sampling)
|
||||
|
||||
def updateSignalBuffer(self):
|
||||
for rowIndex, row in enumerate(self.signalBuffer):
|
||||
self.signalBuffer[rowIndex,:] = 100.*numpy.sin( 2.*numpy.pi*(rowIndex+1.)*self.timeBuffer )
|
||||
|
||||
def sendSignalBufferToOpenvibe(self):
|
||||
start = self.timeBuffer[0]
|
||||
end = self.timeBuffer[-1] + 1./self.sampling
|
||||
bufferElements = self.signalBuffer.reshape(self.nChannel*self.epochSampleCount).tolist()
|
||||
self.output[0].append( OVSignalBuffer(start, end, bufferElements) )
|
||||
|
||||
def process(self):
|
||||
start = self.timeBuffer[0]
|
||||
end = self.timeBuffer[-1]
|
||||
if self.getCurrentTime() >= end:
|
||||
self.sendSignalBufferToOpenvibe()
|
||||
self.updateStartTime()
|
||||
self.updateEndTime()
|
||||
self.updateTimeBuffer()
|
||||
self.updateSignalBuffer()
|
||||
|
||||
def uninitialize(self):
|
||||
end = self.timeBuffer[-1]
|
||||
self.output[0].append(OVSignalEnd(end, end))
|
||||
|
||||
box = MyOVBox()
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
from multiprocessing import Process, Event
|
||||
import time
|
||||
import pygame
|
||||
from pygame.locals import *
|
||||
import math
|
||||
from random import randint, randrange
|
||||
import sys
|
||||
sys.argv = ['openvibe']
|
||||
|
||||
class OVProcess(Process):
|
||||
def __init__(self):
|
||||
Process.__init__(self)
|
||||
self.initialized = Event()
|
||||
self.stop_asked = Event()
|
||||
|
||||
def initialize(self):
|
||||
pass
|
||||
|
||||
def process(self):
|
||||
pass
|
||||
|
||||
def uninitialize(self):
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
self.stop_asked.set()
|
||||
|
||||
def run(self):
|
||||
self.initialize()
|
||||
self.initialized.set()
|
||||
while not self.stop_asked.is_set():
|
||||
self.process()
|
||||
self.uninitialize()
|
||||
|
||||
|
||||
class StarField(OVProcess):
|
||||
def __init__(self, num_stars, max_depth):
|
||||
OVProcess.__init__(self)
|
||||
self.pygame_is_running = False
|
||||
self.screen = None
|
||||
self.clock = None
|
||||
self.num_stars = num_stars
|
||||
self.max_depth = max_depth
|
||||
self.stars = []
|
||||
for i in range(self.num_stars):
|
||||
star = [randrange(-25,25), randrange(-25,25), randrange(1, self.max_depth)]
|
||||
self.stars.append(star)
|
||||
|
||||
def initialize(self):
|
||||
pygame.init()
|
||||
self.pygame_is_running = True
|
||||
self.screen = pygame.display.set_mode((640, 480))
|
||||
pygame.display.set_caption("3D Starfield Simulation")
|
||||
self.clock = pygame.time.Clock()
|
||||
|
||||
def move_and_draw_stars(self):
|
||||
origin_x = self.screen.get_width() / 2
|
||||
origin_y = self.screen.get_height() / 2
|
||||
for star in self.stars:
|
||||
star[2] -= 0.19
|
||||
if star[2] <= 0:
|
||||
star[0] = randrange(-25,25)
|
||||
star[1] = randrange(-25,25)
|
||||
star[2] = self.max_depth
|
||||
k = 128.0 / star[2]
|
||||
x = int(star[0] * k + origin_x)
|
||||
y = int(star[1] * k + origin_y)
|
||||
if 0 <= x < self.screen.get_width() and 0 <= y < self.screen.get_height():
|
||||
size = (1 - float(star[2]) / self.max_depth) * 5
|
||||
shade = (1 - float(star[2]) / self.max_depth) * 255
|
||||
self.screen.fill((shade,shade,shade),(x,y,size,size))
|
||||
|
||||
def process(self):
|
||||
if self.pygame_is_running:
|
||||
self.clock.tick(50)
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
self.pygame_is_running = False
|
||||
return
|
||||
self.screen.fill((0,0,0))
|
||||
self.move_and_draw_stars()
|
||||
pygame.display.flip()
|
||||
|
||||
def uninitialize(self):
|
||||
if self.pygame_is_running:
|
||||
pygame.quit()
|
||||
self.pygame_is_running = False
|
||||
|
||||
class MyOVBox(OVBox):
|
||||
def __init__(self):
|
||||
OVBox.__init__(self)
|
||||
self.p = None
|
||||
def initialize(self):
|
||||
self.p = StarField(512, 32)
|
||||
self.p.start()
|
||||
|
||||
def process(self):
|
||||
pass
|
||||
|
||||
def uninitialize(self):
|
||||
self.p.stop()
|
||||
self.p.join()
|
||||
|
||||
if __name__ == '__main__':
|
||||
box = MyOVBox()
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* \page BoxAlgorithm_Python2Scripting Python 2 scripting
|
||||
__________________________________________________________________
|
||||
|
||||
Detailed description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Python2Scripting_Description|
|
||||
This box can process data from and to OpenViBE using Python script.
|
||||
Available IO types are Streamed Matrix, Signal and Stimulations.
|
||||
|
||||
The user Python Script must define a new class that inherits from OVBox,
|
||||
and implements the initialize, process and uninitialize methods.
|
||||
|
||||
User script must end with : box = MyOVBox() where MyOVBox is the new class.
|
||||
|
||||
Please look at <a href="http://openvibe.inria.fr/tutorial-using-python-with-openvibe">the documentation page</a> for more details.
|
||||
|
||||
* |OVP_DocEnd_BoxAlgorithm_Python2Scripting_Description|
|
||||
__________________________________________________________________
|
||||
|
||||
Settings description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Python2Scripting_Settings|
|
||||
* |OVP_DocEnd_BoxAlgorithm_Python2Scripting_Settings|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Python2Scripting_Setting1|
|
||||
The box clock frequency. The Python process function is called at each tick.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Python2Scripting_Setting1|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Python2Scripting_Setting2|
|
||||
The Python script file.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Python2Scripting_Setting2|
|
||||
__________________________________________________________________
|
||||
|
||||
Examples description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Python2Scripting_Examples|
|
||||
See <a href="http://openvibe.inria.fr/tutorial-using-python-with-openvibe">this page</a> for commented examples.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Python2Scripting_Examples|
|
||||
__________________________________________________________________
|
||||
|
||||
Miscellaneous description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Python2Scripting_Miscellaneous|
|
||||
* |OVP_DocEnd_BoxAlgorithm_Python2Scripting_Miscellaneous|
|
||||
*/
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#File Name : StimulationsCodes.py
|
||||
#Created By : Aurelien Van Langhenhove
|
||||
|
||||
## Stimulation codes
|
||||
## Originally from openvibe-toolkit/ovtk_defines.h
|
||||
|
||||
OpenViBE_stimulation = {
|
||||
'OVTK_StimulationId_ExperimentStart' : 0x00008001,
|
||||
'OVTK_StimulationId_ExperimentStop' : 0x00008002,
|
||||
'OVTK_StimulationId_SegmentStart' : 0x00008003,
|
||||
'OVTK_StimulationId_SegmentStop' : 0x00008004,
|
||||
'OVTK_StimulationId_TrialStart' : 0x00008005,
|
||||
'OVTK_StimulationId_TrialStop' : 0x00008006,
|
||||
'OVTK_StimulationId_BaselineStart' : 0x00008007,
|
||||
'OVTK_StimulationId_BaselineStop' : 0x00008008,
|
||||
'OVTK_StimulationId_RestStart' : 0x00008009,
|
||||
'OVTK_StimulationId_RestStop' : 0x0000800a,
|
||||
'OVTK_StimulationId_VisualStimulationStart' : 0x0000800b,
|
||||
'OVTK_StimulationId_VisualStimulationStop' : 0x0000800c,
|
||||
'OVTK_StimulationId_VisualSteadyStateStimulationStart' : 0x00008010,
|
||||
'OVTK_StimulationId_VisualSteadyStateStimulationStop' : 0x00008011,
|
||||
|
||||
'OVTK_StimulationId_RemovedSamples' : 0x00008310,
|
||||
'OVTK_StimulationId_AddedSamplesBegin' : 0x00008311,
|
||||
'OVTK_StimulationId_AddedSamplesEnd' : 0x00008312,
|
||||
|
||||
'OVTK_StimulationId_LabelStart' : 0x00008100,
|
||||
'OVTK_StimulationId_Label_00' : 0x00008100,
|
||||
'OVTK_StimulationId_Label_01' : 0x00008101,
|
||||
'OVTK_StimulationId_Label_02' : 0x00008102,
|
||||
'OVTK_StimulationId_Label_03' : 0x00008103,
|
||||
'OVTK_StimulationId_Label_04' : 0x00008104,
|
||||
'OVTK_StimulationId_Label_05' : 0x00008105,
|
||||
'OVTK_StimulationId_Label_06' : 0x00008106,
|
||||
'OVTK_StimulationId_Label_07' : 0x00008107,
|
||||
'OVTK_StimulationId_Label_08' : 0x00008108,
|
||||
'OVTK_StimulationId_Label_09' : 0x00008109,
|
||||
'OVTK_StimulationId_Label_0A' : 0x0000810a,
|
||||
'OVTK_StimulationId_Label_0B' : 0x0000810b,
|
||||
'OVTK_StimulationId_Label_0C' : 0x0000810c,
|
||||
'OVTK_StimulationId_Label_0D' : 0x0000810d,
|
||||
'OVTK_StimulationId_Label_0E' : 0x0000810e,
|
||||
'OVTK_StimulationId_Label_0F' : 0x0000810f,
|
||||
'OVTK_StimulationId_Label_10' : 0x00008110,
|
||||
'OVTK_StimulationId_Label_11' : 0x00008111,
|
||||
'OVTK_StimulationId_Label_12' : 0x00008112,
|
||||
'OVTK_StimulationId_Label_13' : 0x00008113,
|
||||
'OVTK_StimulationId_Label_14' : 0x00008114,
|
||||
'OVTK_StimulationId_Label_15' : 0x00008115,
|
||||
'OVTK_StimulationId_Label_16' : 0x00008116,
|
||||
'OVTK_StimulationId_Label_17' : 0x00008117,
|
||||
'OVTK_StimulationId_Label_18' : 0x00008118,
|
||||
'OVTK_StimulationId_Label_19' : 0x00008119,
|
||||
'OVTK_StimulationId_Label_1A' : 0x0000811a,
|
||||
'OVTK_StimulationId_Label_1B' : 0x0000811b,
|
||||
'OVTK_StimulationId_Label_1C' : 0x0000811c,
|
||||
'OVTK_StimulationId_Label_1D' : 0x0000811d,
|
||||
'OVTK_StimulationId_Label_1E' : 0x0000811e,
|
||||
'OVTK_StimulationId_Label_1F' : 0x0000811f,
|
||||
'OVTK_StimulationId_LabelEnd' : 0x000081ff,
|
||||
|
||||
'OVTK_StimulationId_NumberStart' : 0x00000000,
|
||||
'OVTK_StimulationId_Number_00' : 0x00000000,
|
||||
'OVTK_StimulationId_Number_01' : 0x00000001,
|
||||
'OVTK_StimulationId_Number_02' : 0x00000002,
|
||||
'OVTK_StimulationId_Number_03' : 0x00000003,
|
||||
'OVTK_StimulationId_Number_04' : 0x00000004,
|
||||
'OVTK_StimulationId_Number_05' : 0x00000005,
|
||||
'OVTK_StimulationId_Number_06' : 0x00000006,
|
||||
'OVTK_StimulationId_Number_07' : 0x00000007,
|
||||
'OVTK_StimulationId_Number_08' : 0x00000008,
|
||||
'OVTK_StimulationId_Number_09' : 0x00000009,
|
||||
'OVTK_StimulationId_Number_0A' : 0x0000000a,
|
||||
'OVTK_StimulationId_Number_0B' : 0x0000000b,
|
||||
'OVTK_StimulationId_Number_0C' : 0x0000000c,
|
||||
'OVTK_StimulationId_Number_0D' : 0x0000000d,
|
||||
'OVTK_StimulationId_Number_0E' : 0x0000000e,
|
||||
'OVTK_StimulationId_Number_0F' : 0x0000000f,
|
||||
'OVTK_StimulationId_Number_10' : 0x00000010,
|
||||
'OVTK_StimulationId_Number_11' : 0x00000011,
|
||||
'OVTK_StimulationId_Number_12' : 0x00000012,
|
||||
'OVTK_StimulationId_Number_13' : 0x00000013,
|
||||
'OVTK_StimulationId_Number_14' : 0x00000014,
|
||||
'OVTK_StimulationId_Number_15' : 0x00000015,
|
||||
'OVTK_StimulationId_Number_16' : 0x00000016,
|
||||
'OVTK_StimulationId_Number_17' : 0x00000017,
|
||||
'OVTK_StimulationId_Number_18' : 0x00000018,
|
||||
'OVTK_StimulationId_Number_19' : 0x00000019,
|
||||
'OVTK_StimulationId_Number_1A' : 0x0000001a,
|
||||
'OVTK_StimulationId_Number_1B' : 0x0000001b,
|
||||
'OVTK_StimulationId_Number_1C' : 0x0000001c,
|
||||
'OVTK_StimulationId_Number_1D' : 0x0000001d,
|
||||
'OVTK_StimulationId_Number_1E' : 0x0000001e,
|
||||
'OVTK_StimulationId_Number_1F' : 0x0000001f,
|
||||
'OVTK_StimulationId_NumberEnd' : 0x000000ff,
|
||||
|
||||
'OVTK_StimulationId_Train' : 0x00008201,
|
||||
'OVTK_StimulationId_Beep' : 0x00008202,
|
||||
'OVTK_StimulationId_DoubleBeep' : 0x00008203,
|
||||
'OVTK_StimulationId_EndOfFile' : 0x00008204,
|
||||
'OVTK_StimulationId_Target' : 0x00008205,
|
||||
'OVTK_StimulationId_NonTarget' : 0x00008206,
|
||||
|
||||
'OVTK_GDF_Artifact_EOG_Large' : 0x101,
|
||||
'OVTK_GDF_Artifact_ECG' : 0x102,
|
||||
'OVTK_GDF_Artifact_EMG' : 0x103,
|
||||
'OVTK_GDF_Artifact_Movement' : 0x104,
|
||||
'OVTK_GDF_Artifact_Failing_Electrode' : 0x105,
|
||||
'OVTK_GDF_Artifact_Sweat' : 0x106,
|
||||
'OVTK_GDF_Artifact_50_60_Hz_Interference' : 0x107,
|
||||
'OVTK_GDF_Artifact_Breathing' : 0x108,
|
||||
'OVTK_GDF_Artifact_Pulse' : 0x109,
|
||||
'OVTK_GDF_Artifact_EOG_Small' : 0x10A,
|
||||
|
||||
'OVTK_GDF_Calibration' : 0x10F,
|
||||
|
||||
'OVTK_GDF_EEG_Sleep_Splindles' : 0x111,
|
||||
'OVTK_GDF_EEG_K_Complexes' : 0x112,
|
||||
'OVTK_GDF_EEG_Saw_Tooth_Waves' : 0x113,
|
||||
'OVTK_GDF_EEG_Idling_EEG_Eyes_Open' : 0x114,
|
||||
'OVTK_GDF_EEG_Idling_EEG_Eyes_Closed' : 0x115,
|
||||
'OVTK_GDF_EEG_Spike' : 0x116,
|
||||
'OVTK_GDF_EEG_Seizure' : 0x117,
|
||||
|
||||
'OVTK_GDF_VEP' : 0x121,
|
||||
'OVTK_GDF_AEP' : 0x122,
|
||||
'OVTK_GDF_SEP' : 0x123,
|
||||
'OVTK_GDF_TMS' : 0x12F,
|
||||
|
||||
'OVTK_GDF_SSVEP' : 0x131,
|
||||
'OVTK_GDF_SSAEP' : 0x132,
|
||||
'OVTK_GDF_SSSEP' : 0x133,
|
||||
|
||||
'OVTK_GDF_Start_Of_Trial' : 0x300,
|
||||
'OVTK_GDF_Left' : 0x301,
|
||||
'OVTK_GDF_Right' : 0x302,
|
||||
'OVTK_GDF_Foot' : 0x303,
|
||||
'OVTK_GDF_Tongue' : 0x304,
|
||||
'OVTK_GDF_class5' : 0x305,
|
||||
'OVTK_GDF_Down' : 0x306,
|
||||
'OVTK_GDF_class7' : 0x307,
|
||||
'OVTK_GDF_class8' : 0x308,
|
||||
'OVTK_GDF_class9' : 0x309,
|
||||
'OVTK_GDF_class10' : 0x30A,
|
||||
'OVTK_GDF_class11' : 0x30B,
|
||||
'OVTK_GDF_Up' : 0x30C,
|
||||
'OVTK_GDF_Feedback_Continuous' : 0x30D,
|
||||
'OVTK_GDF_Feedback_Discrete' : 0x30E,
|
||||
'OVTK_GDF_Cue_Unknown_Undefined' : 0x30F,
|
||||
'OVTK_GDF_Beep' : 0x311,
|
||||
'OVTK_GDF_Cross_On_Screen' : 0x312,
|
||||
'OVTK_GDF_Flashing_Light' : 0x313,
|
||||
|
||||
'OVTK_GDF_End_Of_Trial' : 0x320,
|
||||
|
||||
'OVTK_GDF_Correct' : 0x381,
|
||||
'OVTK_GDF_Incorrect' : 0x382,
|
||||
|
||||
'OVTK_GDF_End_Of_Session' : 0x3F2,
|
||||
'OVTK_GDF_Rejection' : 0x3FF,
|
||||
|
||||
'OVTK_GDF_OAHE' : 0x401,
|
||||
'OVTK_GDF_RERA' : 0x402,
|
||||
'OVTK_GDF_CAHE' : 0x403,
|
||||
'OVTK_GDF_CSB' : 0x404,
|
||||
'OVTK_GDF_Sleep_Hypoventilation' : 0x405,
|
||||
'OVTK_GDF_Maximum_Inspiration' : 0x40E,
|
||||
'OVTK_GDF_Start_Of_Inspiration' : 0x40F,
|
||||
|
||||
'OVTK_GDF_Wake' : 0x410,
|
||||
'OVTK_GDF_Stage_1' : 0x411,
|
||||
'OVTK_GDF_Stage_2' : 0x412,
|
||||
'OVTK_GDF_Stage_3' : 0x413,
|
||||
'OVTK_GDF_Stage_4' : 0x414,
|
||||
'OVTK_GDF_REM' : 0x415,
|
||||
|
||||
'OVTK_GDF_Lights_On' : 0x420,
|
||||
'OVTK_GDF_Lights_Off' : 0x8420,
|
||||
|
||||
'OVTK_GDF_Eyes_Left' : 0x431,
|
||||
'OVTK_GDF_Eyes_Right' : 0x432,
|
||||
'OVTK_GDF_Eyes_Up' : 0x433,
|
||||
'OVTK_GDF_Eyes_Down' : 0x434,
|
||||
'OVTK_GDF_Horizontal_Eye_Movement' : 0x435,
|
||||
'OVTK_GDF_Vertical_Eye_Movement' : 0x436,
|
||||
'OVTK_GDF_Rotation_Clockwise' : 0x437,
|
||||
'OVTK_GDF_Rotation_Counterclockwise' : 0x438,
|
||||
'OVTK_GDF_Eye_Blink' : 0x439,
|
||||
|
||||
'OVTK_GDF_Left_Hand_Movement' : 0x441,
|
||||
'OVTK_GDF_Right_Hand_Movement' : 0x442,
|
||||
'OVTK_GDF_Head_Movement' : 0x443,
|
||||
'OVTK_GDF_Tongue_Movement' : 0x444,
|
||||
'OVTK_GDF_Swallowing' : 0x445,
|
||||
'OVTK_GDF_Biting' : 0x446,
|
||||
'OVTK_GDF_Foot_Movement' : 0x447,
|
||||
'OVTK_GDF_Foot_Right_Movement' : 0x448,
|
||||
'OVTK_GDF_Arm_Movement' : 0x449,
|
||||
'OVTK_GDF_Arm_Right_Movement' : 0x44A,
|
||||
|
||||
'OVTK_GDF_ECG_Fiducial_Point_QRS_Complex' : 0x501,
|
||||
'OVTK_GDF_ECG_P_Wave' : 0x502,
|
||||
'OVTK_GDF_ECG_QRS_Complex' : 0x503,
|
||||
'OVTK_GDF_ECG_R_Point' : 0x504,
|
||||
'OVTK_GDF_ECG_T_Wave' : 0x506,
|
||||
'OVTK_GDF_ECG_U_Wave' : 0x507,
|
||||
|
||||
'OVTK_GDF_Start' : 0x580,
|
||||
'OVTK_GDF_25_Watt' : 0x581,
|
||||
'OVTK_GDF_50_Watt' : 0x582,
|
||||
'OVTK_GDF_75_Watt' : 0x583,
|
||||
'OVTK_GDF_100_Watt' : 0x584,
|
||||
'OVTK_GDF_125_Watt' : 0x585,
|
||||
'OVTK_GDF_150_Watt' : 0x586,
|
||||
'OVTK_GDF_175_Watt' : 0x587,
|
||||
'OVTK_GDF_200_Watt' : 0x588,
|
||||
'OVTK_GDF_225_Watt' : 0x589,
|
||||
'OVTK_GDF_250_Watt' : 0x58A,
|
||||
'OVTK_GDF_275_Watt' : 0x58B,
|
||||
'OVTK_GDF_300_Watt' : 0x58C,
|
||||
'OVTK_GDF_325_Watt' : 0x58D,
|
||||
'OVTK_GDF_350_Watt' : 0x58E,
|
||||
|
||||
'OVTK_GDF_Start_Of_New_Segment' : 0x7FFE,
|
||||
'OVTK_GDF_Non_Equidistant_Sampling_Value' : 0x7FFF
|
||||
}
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#File Name : openvibe.py
|
||||
#Created By : Aurelien Van Langhenhove
|
||||
|
||||
from StimulationsCodes import *
|
||||
import sys, traceback, collections
|
||||
from StringIO import StringIO
|
||||
|
||||
class NewStd(StringIO):
|
||||
def __init__(self):
|
||||
StringIO.__init__(self)
|
||||
def flush(self):
|
||||
self.buf = str()
|
||||
def isempty(self):
|
||||
if len(self.buf) == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
sys.stdout = NewStd()
|
||||
sys.stderr = NewStd()
|
||||
|
||||
|
||||
def execfileHandlingException(filename, maindictionary):
|
||||
print "executing script file"
|
||||
|
||||
try:
|
||||
maindictionary['box'] = OVBox(default=True) # on en cree une au cas ou
|
||||
#print globals()
|
||||
execfile(filename, maindictionary)
|
||||
return 0
|
||||
except:
|
||||
print "error"
|
||||
print traceback.format_exc()
|
||||
return -1
|
||||
|
||||
def decoratorFunction(target):
|
||||
""" add a try except block to protect openvibe box in case of exception """
|
||||
def wrapper(self):
|
||||
try :
|
||||
print "using decorator"
|
||||
print 'Calling function "%s"' % target.__name__
|
||||
return target(self)
|
||||
except:
|
||||
print traceback.format_exc()
|
||||
|
||||
return wrapper
|
||||
|
||||
class OVChunk(object):
|
||||
def __init__(self, startTime, endTime):
|
||||
self.startTime = startTime
|
||||
self.endTime = endTime
|
||||
|
||||
|
||||
class OVStreamedMatrixHeader(OVChunk):
|
||||
def __init__(self, startTime, endTime, dimensionSizes, dimensionLabels):
|
||||
OVChunk.__init__(self, startTime, endTime)
|
||||
self.dimensionSizes = list(dimensionSizes)
|
||||
self.dimensionLabels = list(dimensionLabels)
|
||||
|
||||
def getDimensionCount(self):
|
||||
return len(self.dimensionSizes)
|
||||
|
||||
def getBufferElementCount(self):
|
||||
elementCount = 0
|
||||
for dimension, size in enumerate(self.dimensionSizes):
|
||||
if dimension == 0:
|
||||
elementCount = int(size)
|
||||
else:
|
||||
elementCount *= int(size)
|
||||
return elementCount
|
||||
|
||||
class OVStreamedMatrixBuffer(OVChunk, list):
|
||||
def __init__(self, startTime, endTime, bufferElements):
|
||||
OVChunk.__init__(self, startTime, endTime)
|
||||
list.__init__(self, bufferElements)
|
||||
|
||||
class OVStreamedMatrixEnd(OVChunk):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class OVSignalHeader(OVStreamedMatrixHeader):
|
||||
def __init__(self, startTime, endTime, dimensionSizes, dimensionLabels, samplingRate):
|
||||
OVStreamedMatrixHeader.__init__(self, startTime, endTime, dimensionSizes, dimensionLabels)
|
||||
self.samplingRate = int(samplingRate)
|
||||
|
||||
class OVSignalBuffer(OVStreamedMatrixBuffer):
|
||||
pass
|
||||
|
||||
class OVSignalEnd(OVChunk):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class OVStimulation(object):
|
||||
def __init__(self, identifier, date, duration):
|
||||
self.identifier = identifier
|
||||
self.date = date
|
||||
self.duration = duration
|
||||
|
||||
class OVStimulationHeader(OVChunk):
|
||||
pass
|
||||
|
||||
class OVStimulationSet(OVChunk, list):
|
||||
def __init__(self, startTime, endTime):
|
||||
list.__init__(self)
|
||||
OVChunk.__init__(self, startTime, endTime)
|
||||
|
||||
def append(self, item):
|
||||
if isinstance(item, OVStimulation):
|
||||
list.append(self, item)
|
||||
else:
|
||||
raise TypeError("The item must be an OVStimulation")
|
||||
|
||||
class OVStimulationEnd(OVChunk):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class OVBuffer(object):
|
||||
def __init__(self, inputType):
|
||||
self.__deque = collections.deque()
|
||||
self.__type = inputType
|
||||
def __len__(self):
|
||||
return len(self.__deque)
|
||||
def __getitem__(self, key):
|
||||
return self.__deque[key]
|
||||
def __setitem__(self, key, item):
|
||||
self.__deque[key] = item
|
||||
def __delitem__(self, key):
|
||||
del self.__deque[key]
|
||||
def append(self, toAppend):
|
||||
self.__deque.appendleft(toAppend)
|
||||
def pop(self):
|
||||
return self.__deque.pop()
|
||||
def type(self):
|
||||
return self.__type
|
||||
|
||||
|
||||
class OVBox(object):
|
||||
def __init__(self, default=False):
|
||||
self.input = list()
|
||||
self.output = list()
|
||||
self.setting = dict()
|
||||
self.var = dict()
|
||||
self._clock = 0
|
||||
self._currentTime = 0.
|
||||
self.default= default
|
||||
def addInput(self, inputType):
|
||||
self.input.append(OVBuffer(inputType))
|
||||
def addOutput(self, outputType):
|
||||
self.output.append(OVBuffer(outputType))
|
||||
def getClock(self):
|
||||
return self._clock
|
||||
def getCurrentTime(self):
|
||||
return self._currentTime
|
||||
def initialize(self):
|
||||
if self.default == True:
|
||||
print "The box instance has not been created by user script, using default one from openvibe.py (dummy box)."
|
||||
pass
|
||||
def process(self):
|
||||
pass
|
||||
def uninitialize(self):
|
||||
pass
|
||||
def realInitialize(self):
|
||||
try :
|
||||
self.initialize()
|
||||
except:
|
||||
print traceback.format_exc()
|
||||
def realProcess(self):
|
||||
try :
|
||||
self.process()
|
||||
except:
|
||||
print traceback.format_exc()
|
||||
def realUninitialize(self):
|
||||
try :
|
||||
self.uninitialize()
|
||||
except:
|
||||
print traceback.format_exc()
|
||||
+1667
File diff suppressed because it is too large
Load Diff
+146
@@ -0,0 +1,146 @@
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyPython2
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 2)
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
//#include <map>
|
||||
|
||||
namespace OpenViBE
|
||||
{
|
||||
namespace Plugins
|
||||
{
|
||||
namespace Python
|
||||
{
|
||||
class CBoxAlgorithmPython2 final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
void release() override { delete this; }
|
||||
|
||||
uint64_t getClockFrequency() override;
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processClock(Kernel::CMessageClock& msg) override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_Python2)
|
||||
|
||||
protected:
|
||||
|
||||
uint64_t m_clockFrequency = 0;
|
||||
CString m_scriptFilename;
|
||||
|
||||
std::vector<Toolkit::TDecoder<CBoxAlgorithmPython2>*> m_decoders;
|
||||
std::vector<Toolkit::TEncoder<CBoxAlgorithmPython2>*> m_encoders;
|
||||
|
||||
//std::map<char,PyObject *> m_PyObjectMap;
|
||||
PyObject *m_box = nullptr, *m_boxInput = nullptr, *m_boxOutput = nullptr, *m_boxSetting = nullptr,
|
||||
*m_boxCurrentTime = nullptr, *m_boxInitialize = nullptr, *m_boxProcess = nullptr, *m_boxUninitialize = nullptr;
|
||||
bool m_initializeSucceeded = false;
|
||||
|
||||
|
||||
bool logSysStd(const bool out);
|
||||
bool logSysStdout() { return logSysStd(true); }
|
||||
bool logSysStderr() { return logSysStd(false); }
|
||||
void buildPythonSettings();
|
||||
|
||||
bool initializePythonSafely();
|
||||
//bool clearPyObjectMap();
|
||||
bool transferStreamedMatrixInputChunksToPython(const size_t index);
|
||||
bool transferStreamedMatrixOutputChunksFromPython(const size_t index);
|
||||
bool transferSignalInputChunksToPython(const size_t index);
|
||||
bool transferSignalOutputChunksFromPython(const size_t index);
|
||||
bool transferStimulationInputChunksToPython(const size_t index);
|
||||
bool transferStimulationOutputChunksFromPython(const size_t index);
|
||||
|
||||
static bool m_isPythonInitialized;
|
||||
|
||||
// These are all borrowed references in python v2.7. Do not free them.
|
||||
static PyObject *m_mainModule, *m_mainDictionnary;
|
||||
static PyObject *m_matrixHeader, *m_matrixBuffer, *m_matrixEnd;
|
||||
static PyObject *m_signalHeader, *m_signalBuffer, *m_signalEnd;
|
||||
static PyObject *m_stimulationHeader, *m_stimulation, *m_stimulationSet, *m_stimulationEnd;
|
||||
static PyObject* m_buffer;
|
||||
static PyObject* m_execFileFunction;
|
||||
static PyObject *m_stdout, *m_stderr;
|
||||
};
|
||||
|
||||
class CBoxAlgorithmPython2Listener final : public Toolkit::TBoxListener<IBoxListener>
|
||||
{
|
||||
public:
|
||||
bool onInputAdded(Kernel::IBox& box, const size_t index) override
|
||||
{
|
||||
box.setInputType(index, OV_TypeId_StreamedMatrix);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool onOutputAdded(Kernel::IBox& box, const size_t index) override
|
||||
{
|
||||
box.setOutputType(index, OV_TypeId_StreamedMatrix);
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
|
||||
};
|
||||
|
||||
class CBoxAlgorithmPython2Desc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Python 2 scripting"); }
|
||||
CString getAuthorName() const override { return CString("Aurelien Van Langhenhove and Laurent George"); }
|
||||
CString getAuthorCompanyName() const override { return CString("CICIT Garches, Inria"); }
|
||||
CString getShortDescription() const override { return CString("This box executes a python script."); }
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString("Scripting"); }
|
||||
CString getVersion() const override { return CString("0.1"); }
|
||||
CString getStockItemName() const override { return CString("gtk-convert"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_Python2; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmPython2; }
|
||||
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmPython2Listener; }
|
||||
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addSetting("Clock frequency (Hz)", OV_TypeId_Integer, "64");
|
||||
prototype.addSetting("Script", OV_TypeId_Script, "");
|
||||
|
||||
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.addInputSupport(OV_TypeId_Signal);
|
||||
prototype.addInputSupport(OV_TypeId_Stimulations);
|
||||
prototype.addInputSupport(OV_TypeId_StreamedMatrix);
|
||||
|
||||
prototype.addOutputSupport(OV_TypeId_Signal);
|
||||
prototype.addOutputSupport(OV_TypeId_Stimulations);
|
||||
prototype.addOutputSupport(OV_TypeId_StreamedMatrix);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_Python2Desc)
|
||||
};
|
||||
} // namespace Python
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // #if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 2)
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyPython2
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
// Boxes
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#define OVP_ClassId_BoxAlgorithm_Python2 OpenViBE::CIdentifier(0x5DC4F669, 0xD3FD4D64)
|
||||
#define OVP_ClassId_BoxAlgorithm_Python2Desc OpenViBE::CIdentifier(0x404B6FFD, 0x12BDD429)
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
#if defined TARGET_HAS_ThirdPartyPython2 && !(defined(WIN32) && defined(TARGET_BUILDTYPE_Debug))
|
||||
// Windows debug build doesn't typically link as most people don't have the python debug library.
|
||||
|
||||
#include "box-algorithms/ovpCBoxAlgorithmPython2.h"
|
||||
|
||||
#if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 2)
|
||||
|
||||
#ifdef TARGET_OS_Windows
|
||||
#include "windows.h"
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
class CPython2Initializer
|
||||
{
|
||||
public:
|
||||
CPython2Initializer();
|
||||
~CPython2Initializer();
|
||||
bool isPython2Available() const { return m_pythonAvailable; }
|
||||
private:
|
||||
#ifdef TARGET_OS_Windows
|
||||
static bool checkPython2Path();
|
||||
#endif
|
||||
// PyThreadState *m_pMainPyThreadState;
|
||||
bool m_pythonAvailable;
|
||||
};
|
||||
|
||||
#ifdef TARGET_OS_Windows
|
||||
bool CPython2Initializer::checkPython2Path()
|
||||
{
|
||||
std::string path = Py_GetPath();
|
||||
size_t found = path.find_first_of(';');
|
||||
while (found != std::string::npos)
|
||||
{
|
||||
if (found > 0)
|
||||
{
|
||||
std::string filename = path.substr(0, found);
|
||||
const bool exists = (_access(filename.c_str(), 0) == 0);
|
||||
if (exists) { return true; }
|
||||
}
|
||||
path = path.substr(found + 1);
|
||||
found = path.find_first_of(';');
|
||||
}
|
||||
|
||||
std::cout << "Python directory not found. You probably have a corrupted python installation!" << std::endl;
|
||||
std::cout << "The tried path from Py_GetPath() was [" << Py_GetPath() << "]\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
CPython2Initializer::CPython2Initializer() : m_pythonAvailable(false)
|
||||
{
|
||||
#ifdef TARGET_OS_Windows
|
||||
__try
|
||||
{
|
||||
// We do not care about the last file, since it is the OpenViBE runtime path
|
||||
if (!Py_IsInitialized() && checkPython2Path())
|
||||
{
|
||||
Py_Initialize();
|
||||
m_pythonAvailable = true;
|
||||
}
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER) { }
|
||||
#else
|
||||
if (!Py_IsInitialized())
|
||||
{
|
||||
Py_Initialize();
|
||||
m_pythonAvailable = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
CPython2Initializer::~CPython2Initializer()
|
||||
{
|
||||
if (m_pythonAvailable)
|
||||
{
|
||||
m_pythonAvailable = false;
|
||||
Py_Finalize();
|
||||
}
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Python {
|
||||
|
||||
OVP_Declare_Begin()
|
||||
static CPython2Initializer python2Init;
|
||||
if (python2Init.isPython2Available()) { OVP_Declare_New(CBoxAlgorithmPython2Desc); }
|
||||
OVP_Declare_End()
|
||||
|
||||
} // namespace Python
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
#else
|
||||
#pragma message ("WARNING: Python 2.x headers are required to build the Python plugin, different includes found, skipped")
|
||||
#endif // #if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 2)
|
||||
#endif // TARGET_HAS_ThirdPartyPython2
|
||||
@@ -0,0 +1,37 @@
|
||||
PROJECT(openvibe-plugins-contrib-python3)
|
||||
|
||||
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
|
||||
SET(PROJECT_VERSION ${OV_GLOBAL_VERSION_STRING})
|
||||
|
||||
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.inl)
|
||||
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
|
||||
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
FOLDER ${PLUGINS_FOLDER}
|
||||
COMPILE_FLAGS "-DOVP_Exports -DOVP_Shared")
|
||||
|
||||
# ---------------------------------
|
||||
INCLUDE("FindOpenViBE")
|
||||
INCLUDE("FindOpenViBECommon")
|
||||
INCLUDE("FindOpenViBEToolkit")
|
||||
INCLUDE("FindOpenViBEModuleEBML")
|
||||
INCLUDE("FindThirdPartyBoost")
|
||||
INCLUDE("FindThirdPartyPython3")
|
||||
|
||||
# -----------------------------
|
||||
# Install files
|
||||
# -----------------------------
|
||||
INSTALL(TARGETS ${PROJECT_NAME}
|
||||
RUNTIME DESTINATION ${DIST_BINDIR}
|
||||
LIBRARY DESTINATION ${DIST_LIBDIR}
|
||||
ARCHIVE DESTINATION ${DIST_LIBDIR})
|
||||
|
||||
IF(WIN32)
|
||||
# The pygame scenario doesn't work on Windows, so do not install it
|
||||
INSTALL(DIRECTORY box-tutorials DESTINATION ${DIST_DATADIR}/openvibe/scenarios/ PATTERN "*-pygame-*" EXCLUDE)
|
||||
ELSE()
|
||||
INSTALL(DIRECTORY box-tutorials DESTINATION ${DIST_DATADIR}/openvibe/scenarios/)
|
||||
ENDIF()
|
||||
|
||||
INSTALL(DIRECTORY share/ DESTINATION ${DIST_DATADIR}/openvibe/plugins/python3)
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
<OpenViBE-Scenario>
|
||||
<FormatVersion>2</FormatVersion>
|
||||
<Creator>OpenViBE Designer</Creator>
|
||||
<CreatorVersion>2.2.0</CreatorVersion>
|
||||
<Settings></Settings>
|
||||
<Inputs></Inputs>
|
||||
<Outputs></Outputs>
|
||||
<Boxes>
|
||||
<Box>
|
||||
<Identifier>(0x00003a96, 0x00003a53)</Identifier>
|
||||
<Name>Hello World!</Name>
|
||||
<AlgorithmClassIdentifier>(0x5dc4f669, 0xd3fd4d63)</AlgorithmClassIdentifier>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Clock frequency (Hz)</Name>
|
||||
<DefaultValue>64</DefaultValue>
|
||||
<Value>2</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0xb0d0db45, 0x49cbc34a)</TypeIdentifier>
|
||||
<Name>Script</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value>${Player_ScenarioDirectory}/scripts/python-hello-world.py</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<Identifier>(0x78b47164, 0x584420aa)</Identifier>
|
||||
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
|
||||
<Name>Message</Name>
|
||||
<DefaultValue>Hello World!</DefaultValue>
|
||||
<Value>Hello World!</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x17ee7c08, 0x94c14893)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>304</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>112</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x30a4e5c9, 0x83502953)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x2a651510, 0xb4fad0d4)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x61d11811, 0x71e65362)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf191c1c8, 0xa0123976)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
</Boxes>
|
||||
<Links></Links>
|
||||
<Comments>
|
||||
<Comment>
|
||||
<Identifier>(0x000047c8, 0x000050cf)</Identifier>
|
||||
<Text><b>Hello World!</b>
|
||||
|
||||
Prints a friendly message.
|
||||
|
||||
You can customize the message by editing
|
||||
the box configuration.
|
||||
</Text>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x473d9a43, 0x97fc0a97)</Identifier>
|
||||
<Value>-80</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x7234b86b, 0x2b8651a5)</Identifier>
|
||||
<Value>304</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Comment>
|
||||
</Comments>
|
||||
<Metadata>
|
||||
<Entry>
|
||||
<Identifier>(0x0000775c, 0x000078ff)</Identifier>
|
||||
<Type>(0x3bcce5d2, 0x43f2d968)</Type>
|
||||
<Data>[{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"height":507,"identifier":"(0x00003962, 0x00000a21)","name":"Default window","parentIdentifier":"(0xffffffff, 0xffffffff)","type":1,"width":798},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"identifier":"(0x0000781e, 0x00004e65)","index":0,"name":"Default tab","parentIdentifier":"(0x00003962, 0x00000a21)","type":2},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":0,"identifier":"(0x00006808, 0x00000e28)","index":0,"name":"Empty","parentIdentifier":"(0x0000781e, 0x00004e65)","type":0}]</Data>
|
||||
</Entry>
|
||||
</Metadata>
|
||||
</OpenViBE-Scenario>
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
<OpenViBE-Scenario>
|
||||
<FormatVersion>2</FormatVersion>
|
||||
<Creator>OpenViBE Designer</Creator>
|
||||
<CreatorVersion>2.2.0</CreatorVersion>
|
||||
<Settings></Settings>
|
||||
<Inputs></Inputs>
|
||||
<Outputs></Outputs>
|
||||
<Boxes>
|
||||
<Box>
|
||||
<Identifier>(0x00005777, 0x00006c19)</Identifier>
|
||||
<Name>Clock stimulator</Name>
|
||||
<AlgorithmClassIdentifier>(0x4f756d3f, 0x29ff0b96)</AlgorithmClassIdentifier>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Generated stimulations</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Interstimulation interval (in sec)</Name>
|
||||
<DefaultValue>1.0</DefaultValue>
|
||||
<Value>1.0</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
|
||||
<Name>Stimulation</Name>
|
||||
<DefaultValue>OVTK_StimulationId_Label_00</DefaultValue>
|
||||
<Value>OVTK_StimulationId_Label_00</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>48</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>400</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x27b3ee3c, 0xc50527e6)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
|
||||
<Value>(0x00000000, 0x0062599e)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
|
||||
<Value>false</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00006593, 0x00001e93)</Identifier>
|
||||
<Name>Python 3 scripting</Name>
|
||||
<AlgorithmClassIdentifier>(0x5dc4f669, 0xd3fd4d63)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<Identifier>(0xed15d980, 0xc7e2bbc3)</Identifier>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>New input</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Clock frequency (Hz)</Name>
|
||||
<DefaultValue>64</DefaultValue>
|
||||
<Value>64</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0xb0d0db45, 0x49cbc34a)</TypeIdentifier>
|
||||
<Name>Script</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value>${Player_ScenarioDirectory}/scripts/python-print-stimulations.py</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x17ee7c08, 0x94c14893)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>112</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>400</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x30a4e5c9, 0x83502953)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x2a651510, 0xb4fad0d4)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x61d11811, 0x71e65362)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf191c1c8, 0xa0123976)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
</Boxes>
|
||||
<Links>
|
||||
<Link>
|
||||
<Identifier>(0x000061b9, 0x00000505)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00005777, 0x00006c19)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00006593, 0x00001e93)</BoxIdentifier>
|
||||
<BoxInputIdentifier>(0xed15d980, 0xc7e2bbc3)</BoxInputIdentifier>
|
||||
</Target>
|
||||
</Link>
|
||||
</Links>
|
||||
<Comments>
|
||||
<Comment>
|
||||
<Identifier>(0x00003cb4, 0x00004c5e)</Identifier>
|
||||
<Text>The clock stimulator generates
|
||||
1 stim per second.
|
||||
|
||||
The associated python script should
|
||||
print all the received stimulations.</Text>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x473d9a43, 0x97fc0a97)</Identifier>
|
||||
<Value>640.000000</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x7234b86b, 0x2b8651a5)</Identifier>
|
||||
<Value>96.000000</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Comment>
|
||||
</Comments>
|
||||
<Metadata>
|
||||
<Entry>
|
||||
<Identifier>(0x0000775c, 0x000078ff)</Identifier>
|
||||
<Type>(0x3bcce5d2, 0x43f2d968)</Type>
|
||||
<Data>[{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"height":1,"identifier":"(0x000051c6, 0x0000403f)","name":"Default window","parentIdentifier":"(0xffffffff, 0xffffffff)","type":1,"width":1},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"identifier":"(0x00006099, 0x00001a73)","index":0,"name":"Default tab","parentIdentifier":"(0x000051c6, 0x0000403f)","type":2},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":0,"identifier":"(0x00004197, 0x00001dca)","index":0,"name":"Empty","parentIdentifier":"(0x00006099, 0x00001a73)","type":0}]</Data>
|
||||
</Entry>
|
||||
</Metadata>
|
||||
</OpenViBE-Scenario>
|
||||
+1183
File diff suppressed because it is too large
Load Diff
+31
@@ -0,0 +1,31 @@
|
||||
# We construct a box instance that inherits from the basic OVBox class
|
||||
class MyOVBox(OVBox):
|
||||
# the constructor creates the box and initializes object variables
|
||||
def __init__(self):
|
||||
OVBox.__init__(self)
|
||||
self.stimLabel = None
|
||||
self.stimCode = None
|
||||
|
||||
# the initialize method reads settings and outputs the first header
|
||||
def initialize(self):
|
||||
# the stim label is taken from the box setting
|
||||
self.stimLabel = self.setting['Stimulation']
|
||||
# we get the corresponding code using the OpenViBE_stimulation dictionnary
|
||||
self.stimCode = OpenViBE_stimulation[self.stimLabel]
|
||||
# we append to the box output a stimulation header. This is just a header, dates are 0.
|
||||
self.output[0].append(OVStimulationHeader(0., 0.))
|
||||
|
||||
def process(self):
|
||||
# During each process call we produce a stimulation
|
||||
# A stimulation set is a chunk which starts at current time and end time is the time step between two calls
|
||||
stimSet = OVStimulationSet(self.getCurrentTime(), self.getCurrentTime()+1./self.getClock())
|
||||
# the date of the stimulation is simply the current openvibe time when calling the box process
|
||||
stimSet.append(OVStimulation(self.stimCode, self.getCurrentTime(), 0.))
|
||||
self.output[0].append(stimSet)
|
||||
|
||||
def uninitialize(self):
|
||||
# we send a stream end.
|
||||
end = self.getCurrentTime()
|
||||
self.output[0].append(OVStimulationEnd(end, end))
|
||||
|
||||
box = MyOVBox()
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
|
||||
class MyOVBox(OVBox):
|
||||
def __init__(self):
|
||||
OVBox.__init__(self)
|
||||
|
||||
def initialize(self):
|
||||
print('Initialize Hello World Script')
|
||||
# nop
|
||||
return
|
||||
|
||||
def process(self):
|
||||
# print the string specified in the box configuration.
|
||||
# 'Message' is the name of the config entry.
|
||||
print(self.setting['Message'])
|
||||
|
||||
def uninitialize(self):
|
||||
print('Uninitialize Hello World Script')
|
||||
# nop
|
||||
return
|
||||
|
||||
box = MyOVBox()
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
|
||||
class MyOVBox(OVBox):
|
||||
def __init__(self):
|
||||
OVBox.__init__(self)
|
||||
|
||||
def initialize(self):
|
||||
# nop
|
||||
return
|
||||
|
||||
def process(self):
|
||||
for chunkIdx in range( len(self.input[0]) ):
|
||||
chunk = self.input[0].pop()
|
||||
if(type(chunk) == OVStimulationSet):
|
||||
for stimIdx in range(len(chunk)):
|
||||
stim=chunk.pop()
|
||||
print('Received stim', stim.identifier, 'stamped at', stim.date, 's')
|
||||
#else:
|
||||
# print 'Received chunk of type ', type(chunk), " looking for StimulationSet"
|
||||
return
|
||||
|
||||
def uninitialize(self):
|
||||
# nop
|
||||
return
|
||||
|
||||
box = MyOVBox()
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import numpy
|
||||
|
||||
class MyOVBox(OVBox):
|
||||
def __init__(self):
|
||||
OVBox.__init__(self)
|
||||
self.signalHeader = None
|
||||
|
||||
def process(self):
|
||||
for chunkIdx in range( len(self.input[0]) ):
|
||||
if(type(self.input[0][chunkIdx]) == OVSignalHeader):
|
||||
self.signalHeader = self.input[0].pop()
|
||||
|
||||
outputHeader = OVSignalHeader(self.signalHeader.startTime, self.signalHeader.endTime, [1, self.signalHeader.dimensionSizes[1]], ['Mean']+self.signalHeader.dimensionSizes[1]*[''], self.signalHeader.samplingRate)
|
||||
|
||||
self.output[0].append(outputHeader)
|
||||
|
||||
elif(type(self.input[0][chunkIdx]) == OVSignalBuffer):
|
||||
chunk = self.input[0].pop()
|
||||
numpyBuffer = numpy.array(chunk).reshape(tuple(self.signalHeader.dimensionSizes))
|
||||
numpyBuffer = numpyBuffer.mean(axis=0)
|
||||
chunk = OVSignalBuffer(chunk.startTime, chunk.endTime, numpyBuffer.tolist())
|
||||
self.output[0].append(chunk)
|
||||
|
||||
elif(type(self.input[0][chunkIdx]) == OVSignalEnd):
|
||||
self.output[0].append(self.input[0].pop())
|
||||
|
||||
box = MyOVBox()
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
import numpy
|
||||
|
||||
class MyOVBox(OVBox):
|
||||
def __init__(self):
|
||||
OVBox.__init__(self)
|
||||
self.nChannel = 0
|
||||
self.sampling = 0
|
||||
self.epochSampleCount = 0
|
||||
self.startTime = 0.
|
||||
self.endTime = 0.
|
||||
self.dimensionSizes = list()
|
||||
self.dimensionLabels = list()
|
||||
self.timeBuffer = list()
|
||||
self.signalBuffer = None
|
||||
self.signalHeader = None
|
||||
|
||||
def initialize(self):
|
||||
self.nChannel = int(self.setting['Channel count'])
|
||||
self.sampling = int(self.setting['Sampling frequency'])
|
||||
self.epochSampleCount = int(self.setting['Generated epoch sample count'])
|
||||
|
||||
#creation of the signal header
|
||||
for i in range(self.nChannel):
|
||||
self.dimensionLabels.append( 'Sinus'+str(i) )
|
||||
self.dimensionLabels += self.epochSampleCount*['']
|
||||
self.dimensionSizes = [self.nChannel, self.epochSampleCount]
|
||||
self.signalHeader = OVSignalHeader(0., 0., self.dimensionSizes, self.dimensionLabels, self.sampling)
|
||||
self.output[0].append(self.signalHeader)
|
||||
|
||||
#creation of the first signal chunk
|
||||
self.endTime = 1.*self.epochSampleCount/self.sampling
|
||||
self.signalBuffer = numpy.zeros((self.nChannel, self.epochSampleCount))
|
||||
self.updateTimeBuffer()
|
||||
self.updateSignalBuffer()
|
||||
|
||||
def updateStartTime(self):
|
||||
self.startTime += 1.*self.epochSampleCount/self.sampling
|
||||
|
||||
def updateEndTime(self):
|
||||
self.endTime = float(self.startTime + 1.*self.epochSampleCount/self.sampling)
|
||||
|
||||
def updateTimeBuffer(self):
|
||||
self.timeBuffer = numpy.arange(self.startTime, self.endTime, 1./self.sampling)
|
||||
|
||||
def updateSignalBuffer(self):
|
||||
for rowIndex, row in enumerate(self.signalBuffer):
|
||||
self.signalBuffer[rowIndex,:] = 100.*numpy.sin( 2.*numpy.pi*(rowIndex+1.)*self.timeBuffer )
|
||||
|
||||
def sendSignalBufferToOpenvibe(self):
|
||||
start = self.timeBuffer[0]
|
||||
end = self.timeBuffer[-1] + 1./self.sampling
|
||||
bufferElements = self.signalBuffer.reshape(self.nChannel*self.epochSampleCount).tolist()
|
||||
self.output[0].append( OVSignalBuffer(start, end, bufferElements) )
|
||||
|
||||
def process(self):
|
||||
start = self.timeBuffer[0]
|
||||
end = self.timeBuffer[-1]
|
||||
if self.getCurrentTime() >= end:
|
||||
self.sendSignalBufferToOpenvibe()
|
||||
self.updateStartTime()
|
||||
self.updateEndTime()
|
||||
self.updateTimeBuffer()
|
||||
self.updateSignalBuffer()
|
||||
|
||||
def uninitialize(self):
|
||||
end = self.timeBuffer[-1]
|
||||
self.output[0].append(OVSignalEnd(end, end))
|
||||
|
||||
box = MyOVBox()
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* \page BoxAlgorithm_Python3Scripting Python 3 scripting
|
||||
__________________________________________________________________
|
||||
|
||||
Detailed description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Python3Scripting_Description|
|
||||
This box can process data from and to OpenViBE using Python script.
|
||||
Available IO types are Streamed Matrix, Signal and Stimulations.
|
||||
|
||||
The user Python Script must define a new class that inherits from OVBox,
|
||||
and implements the initialize, process and uninitialize methods.
|
||||
|
||||
User script must end with : box = MyOVBox() where MyOVBox is the new class.
|
||||
|
||||
Please look at <a href="http://openvibe.inria.fr/tutorial-using-python-with-openvibe">the documentation page</a> for more details.
|
||||
|
||||
After version 2.2.0, Python 3 is used by default (Python 2 is no longer officially supported since January 1, 2020). To convert your previous scripts, python 3 has a script called <a href="https://docs.python.org/fr/3/library/2to3.html">2to3</a>.
|
||||
|
||||
* |OVP_DocEnd_BoxAlgorithm_Python3Scripting_Description|
|
||||
__________________________________________________________________
|
||||
|
||||
Settings description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Python3Scripting_Settings|
|
||||
* |OVP_DocEnd_BoxAlgorithm_Python3Scripting_Settings|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Python3Scripting_Setting1|
|
||||
The box clock frequency. The Python process function is called at each tick.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Python3Scripting_Setting1|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Python3Scripting_Setting2|
|
||||
The Python script file.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Python3Scripting_Setting2|
|
||||
__________________________________________________________________
|
||||
|
||||
Examples description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Python3Scripting_Examples|
|
||||
See <a href="http://openvibe.inria.fr/tutorial-using-python-with-openvibe">this page</a> for commented examples.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Python3Scripting_Examples|
|
||||
__________________________________________________________________
|
||||
|
||||
Miscellaneous description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Python3Scripting_Miscellaneous|
|
||||
* |OVP_DocEnd_BoxAlgorithm_Python3Scripting_Miscellaneous|
|
||||
*/
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#File Name : StimulationsCodes.py
|
||||
#Created By : Aurelien Van Langhenhove
|
||||
|
||||
## Stimulation codes
|
||||
## Originally from openvibe-toolkit/ovtk_defines.h
|
||||
|
||||
OpenViBE_stimulation = {
|
||||
'OVTK_StimulationId_ExperimentStart' : 0x00008001,
|
||||
'OVTK_StimulationId_ExperimentStop' : 0x00008002,
|
||||
'OVTK_StimulationId_SegmentStart' : 0x00008003,
|
||||
'OVTK_StimulationId_SegmentStop' : 0x00008004,
|
||||
'OVTK_StimulationId_TrialStart' : 0x00008005,
|
||||
'OVTK_StimulationId_TrialStop' : 0x00008006,
|
||||
'OVTK_StimulationId_BaselineStart' : 0x00008007,
|
||||
'OVTK_StimulationId_BaselineStop' : 0x00008008,
|
||||
'OVTK_StimulationId_RestStart' : 0x00008009,
|
||||
'OVTK_StimulationId_RestStop' : 0x0000800a,
|
||||
'OVTK_StimulationId_VisualStimulationStart' : 0x0000800b,
|
||||
'OVTK_StimulationId_VisualStimulationStop' : 0x0000800c,
|
||||
'OVTK_StimulationId_VisualSteadyStateStimulationStart' : 0x00008010,
|
||||
'OVTK_StimulationId_VisualSteadyStateStimulationStop' : 0x00008011,
|
||||
|
||||
'OVTK_StimulationId_RemovedSamples' : 0x00008310,
|
||||
'OVTK_StimulationId_AddedSamplesBegin' : 0x00008311,
|
||||
'OVTK_StimulationId_AddedSamplesEnd' : 0x00008312,
|
||||
|
||||
'OVTK_StimulationId_LabelStart' : 0x00008100,
|
||||
'OVTK_StimulationId_Label_00' : 0x00008100,
|
||||
'OVTK_StimulationId_Label_01' : 0x00008101,
|
||||
'OVTK_StimulationId_Label_02' : 0x00008102,
|
||||
'OVTK_StimulationId_Label_03' : 0x00008103,
|
||||
'OVTK_StimulationId_Label_04' : 0x00008104,
|
||||
'OVTK_StimulationId_Label_05' : 0x00008105,
|
||||
'OVTK_StimulationId_Label_06' : 0x00008106,
|
||||
'OVTK_StimulationId_Label_07' : 0x00008107,
|
||||
'OVTK_StimulationId_Label_08' : 0x00008108,
|
||||
'OVTK_StimulationId_Label_09' : 0x00008109,
|
||||
'OVTK_StimulationId_Label_0A' : 0x0000810a,
|
||||
'OVTK_StimulationId_Label_0B' : 0x0000810b,
|
||||
'OVTK_StimulationId_Label_0C' : 0x0000810c,
|
||||
'OVTK_StimulationId_Label_0D' : 0x0000810d,
|
||||
'OVTK_StimulationId_Label_0E' : 0x0000810e,
|
||||
'OVTK_StimulationId_Label_0F' : 0x0000810f,
|
||||
'OVTK_StimulationId_Label_10' : 0x00008110,
|
||||
'OVTK_StimulationId_Label_11' : 0x00008111,
|
||||
'OVTK_StimulationId_Label_12' : 0x00008112,
|
||||
'OVTK_StimulationId_Label_13' : 0x00008113,
|
||||
'OVTK_StimulationId_Label_14' : 0x00008114,
|
||||
'OVTK_StimulationId_Label_15' : 0x00008115,
|
||||
'OVTK_StimulationId_Label_16' : 0x00008116,
|
||||
'OVTK_StimulationId_Label_17' : 0x00008117,
|
||||
'OVTK_StimulationId_Label_18' : 0x00008118,
|
||||
'OVTK_StimulationId_Label_19' : 0x00008119,
|
||||
'OVTK_StimulationId_Label_1A' : 0x0000811a,
|
||||
'OVTK_StimulationId_Label_1B' : 0x0000811b,
|
||||
'OVTK_StimulationId_Label_1C' : 0x0000811c,
|
||||
'OVTK_StimulationId_Label_1D' : 0x0000811d,
|
||||
'OVTK_StimulationId_Label_1E' : 0x0000811e,
|
||||
'OVTK_StimulationId_Label_1F' : 0x0000811f,
|
||||
'OVTK_StimulationId_LabelEnd' : 0x000081ff,
|
||||
|
||||
'OVTK_StimulationId_NumberStart' : 0x00000000,
|
||||
'OVTK_StimulationId_Number_00' : 0x00000000,
|
||||
'OVTK_StimulationId_Number_01' : 0x00000001,
|
||||
'OVTK_StimulationId_Number_02' : 0x00000002,
|
||||
'OVTK_StimulationId_Number_03' : 0x00000003,
|
||||
'OVTK_StimulationId_Number_04' : 0x00000004,
|
||||
'OVTK_StimulationId_Number_05' : 0x00000005,
|
||||
'OVTK_StimulationId_Number_06' : 0x00000006,
|
||||
'OVTK_StimulationId_Number_07' : 0x00000007,
|
||||
'OVTK_StimulationId_Number_08' : 0x00000008,
|
||||
'OVTK_StimulationId_Number_09' : 0x00000009,
|
||||
'OVTK_StimulationId_Number_0A' : 0x0000000a,
|
||||
'OVTK_StimulationId_Number_0B' : 0x0000000b,
|
||||
'OVTK_StimulationId_Number_0C' : 0x0000000c,
|
||||
'OVTK_StimulationId_Number_0D' : 0x0000000d,
|
||||
'OVTK_StimulationId_Number_0E' : 0x0000000e,
|
||||
'OVTK_StimulationId_Number_0F' : 0x0000000f,
|
||||
'OVTK_StimulationId_Number_10' : 0x00000010,
|
||||
'OVTK_StimulationId_Number_11' : 0x00000011,
|
||||
'OVTK_StimulationId_Number_12' : 0x00000012,
|
||||
'OVTK_StimulationId_Number_13' : 0x00000013,
|
||||
'OVTK_StimulationId_Number_14' : 0x00000014,
|
||||
'OVTK_StimulationId_Number_15' : 0x00000015,
|
||||
'OVTK_StimulationId_Number_16' : 0x00000016,
|
||||
'OVTK_StimulationId_Number_17' : 0x00000017,
|
||||
'OVTK_StimulationId_Number_18' : 0x00000018,
|
||||
'OVTK_StimulationId_Number_19' : 0x00000019,
|
||||
'OVTK_StimulationId_Number_1A' : 0x0000001a,
|
||||
'OVTK_StimulationId_Number_1B' : 0x0000001b,
|
||||
'OVTK_StimulationId_Number_1C' : 0x0000001c,
|
||||
'OVTK_StimulationId_Number_1D' : 0x0000001d,
|
||||
'OVTK_StimulationId_Number_1E' : 0x0000001e,
|
||||
'OVTK_StimulationId_Number_1F' : 0x0000001f,
|
||||
'OVTK_StimulationId_NumberEnd' : 0x000000ff,
|
||||
|
||||
'OVTK_StimulationId_Train' : 0x00008201,
|
||||
'OVTK_StimulationId_Beep' : 0x00008202,
|
||||
'OVTK_StimulationId_DoubleBeep' : 0x00008203,
|
||||
'OVTK_StimulationId_EndOfFile' : 0x00008204,
|
||||
'OVTK_StimulationId_Target' : 0x00008205,
|
||||
'OVTK_StimulationId_NonTarget' : 0x00008206,
|
||||
|
||||
'OVTK_GDF_Artifact_EOG_Large' : 0x101,
|
||||
'OVTK_GDF_Artifact_ECG' : 0x102,
|
||||
'OVTK_GDF_Artifact_EMG' : 0x103,
|
||||
'OVTK_GDF_Artifact_Movement' : 0x104,
|
||||
'OVTK_GDF_Artifact_Failing_Electrode' : 0x105,
|
||||
'OVTK_GDF_Artifact_Sweat' : 0x106,
|
||||
'OVTK_GDF_Artifact_50_60_Hz_Interference' : 0x107,
|
||||
'OVTK_GDF_Artifact_Breathing' : 0x108,
|
||||
'OVTK_GDF_Artifact_Pulse' : 0x109,
|
||||
'OVTK_GDF_Artifact_EOG_Small' : 0x10A,
|
||||
|
||||
'OVTK_GDF_Calibration' : 0x10F,
|
||||
|
||||
'OVTK_GDF_EEG_Sleep_Splindles' : 0x111,
|
||||
'OVTK_GDF_EEG_K_Complexes' : 0x112,
|
||||
'OVTK_GDF_EEG_Saw_Tooth_Waves' : 0x113,
|
||||
'OVTK_GDF_EEG_Idling_EEG_Eyes_Open' : 0x114,
|
||||
'OVTK_GDF_EEG_Idling_EEG_Eyes_Closed' : 0x115,
|
||||
'OVTK_GDF_EEG_Spike' : 0x116,
|
||||
'OVTK_GDF_EEG_Seizure' : 0x117,
|
||||
|
||||
'OVTK_GDF_VEP' : 0x121,
|
||||
'OVTK_GDF_AEP' : 0x122,
|
||||
'OVTK_GDF_SEP' : 0x123,
|
||||
'OVTK_GDF_TMS' : 0x12F,
|
||||
|
||||
'OVTK_GDF_SSVEP' : 0x131,
|
||||
'OVTK_GDF_SSAEP' : 0x132,
|
||||
'OVTK_GDF_SSSEP' : 0x133,
|
||||
|
||||
'OVTK_GDF_Start_Of_Trial' : 0x300,
|
||||
'OVTK_GDF_Left' : 0x301,
|
||||
'OVTK_GDF_Right' : 0x302,
|
||||
'OVTK_GDF_Foot' : 0x303,
|
||||
'OVTK_GDF_Tongue' : 0x304,
|
||||
'OVTK_GDF_class5' : 0x305,
|
||||
'OVTK_GDF_Down' : 0x306,
|
||||
'OVTK_GDF_class7' : 0x307,
|
||||
'OVTK_GDF_class8' : 0x308,
|
||||
'OVTK_GDF_class9' : 0x309,
|
||||
'OVTK_GDF_class10' : 0x30A,
|
||||
'OVTK_GDF_class11' : 0x30B,
|
||||
'OVTK_GDF_Up' : 0x30C,
|
||||
'OVTK_GDF_Feedback_Continuous' : 0x30D,
|
||||
'OVTK_GDF_Feedback_Discrete' : 0x30E,
|
||||
'OVTK_GDF_Cue_Unknown_Undefined' : 0x30F,
|
||||
'OVTK_GDF_Beep' : 0x311,
|
||||
'OVTK_GDF_Cross_On_Screen' : 0x312,
|
||||
'OVTK_GDF_Flashing_Light' : 0x313,
|
||||
|
||||
'OVTK_GDF_End_Of_Trial' : 0x320,
|
||||
|
||||
'OVTK_GDF_Correct' : 0x381,
|
||||
'OVTK_GDF_Incorrect' : 0x382,
|
||||
|
||||
'OVTK_GDF_End_Of_Session' : 0x3F2,
|
||||
'OVTK_GDF_Rejection' : 0x3FF,
|
||||
|
||||
'OVTK_GDF_OAHE' : 0x401,
|
||||
'OVTK_GDF_RERA' : 0x402,
|
||||
'OVTK_GDF_CAHE' : 0x403,
|
||||
'OVTK_GDF_CSB' : 0x404,
|
||||
'OVTK_GDF_Sleep_Hypoventilation' : 0x405,
|
||||
'OVTK_GDF_Maximum_Inspiration' : 0x40E,
|
||||
'OVTK_GDF_Start_Of_Inspiration' : 0x40F,
|
||||
|
||||
'OVTK_GDF_Wake' : 0x410,
|
||||
'OVTK_GDF_Stage_1' : 0x411,
|
||||
'OVTK_GDF_Stage_2' : 0x412,
|
||||
'OVTK_GDF_Stage_3' : 0x413,
|
||||
'OVTK_GDF_Stage_4' : 0x414,
|
||||
'OVTK_GDF_REM' : 0x415,
|
||||
|
||||
'OVTK_GDF_Lights_On' : 0x420,
|
||||
'OVTK_GDF_Lights_Off' : 0x8420,
|
||||
|
||||
'OVTK_GDF_Eyes_Left' : 0x431,
|
||||
'OVTK_GDF_Eyes_Right' : 0x432,
|
||||
'OVTK_GDF_Eyes_Up' : 0x433,
|
||||
'OVTK_GDF_Eyes_Down' : 0x434,
|
||||
'OVTK_GDF_Horizontal_Eye_Movement' : 0x435,
|
||||
'OVTK_GDF_Vertical_Eye_Movement' : 0x436,
|
||||
'OVTK_GDF_Rotation_Clockwise' : 0x437,
|
||||
'OVTK_GDF_Rotation_Counterclockwise' : 0x438,
|
||||
'OVTK_GDF_Eye_Blink' : 0x439,
|
||||
|
||||
'OVTK_GDF_Left_Hand_Movement' : 0x441,
|
||||
'OVTK_GDF_Right_Hand_Movement' : 0x442,
|
||||
'OVTK_GDF_Head_Movement' : 0x443,
|
||||
'OVTK_GDF_Tongue_Movement' : 0x444,
|
||||
'OVTK_GDF_Swallowing' : 0x445,
|
||||
'OVTK_GDF_Biting' : 0x446,
|
||||
'OVTK_GDF_Foot_Movement' : 0x447,
|
||||
'OVTK_GDF_Foot_Right_Movement' : 0x448,
|
||||
'OVTK_GDF_Arm_Movement' : 0x449,
|
||||
'OVTK_GDF_Arm_Right_Movement' : 0x44A,
|
||||
|
||||
'OVTK_GDF_ECG_Fiducial_Point_QRS_Complex' : 0x501,
|
||||
'OVTK_GDF_ECG_P_Wave' : 0x502,
|
||||
'OVTK_GDF_ECG_QRS_Complex' : 0x503,
|
||||
'OVTK_GDF_ECG_R_Point' : 0x504,
|
||||
'OVTK_GDF_ECG_T_Wave' : 0x506,
|
||||
'OVTK_GDF_ECG_U_Wave' : 0x507,
|
||||
|
||||
'OVTK_GDF_Start' : 0x580,
|
||||
'OVTK_GDF_25_Watt' : 0x581,
|
||||
'OVTK_GDF_50_Watt' : 0x582,
|
||||
'OVTK_GDF_75_Watt' : 0x583,
|
||||
'OVTK_GDF_100_Watt' : 0x584,
|
||||
'OVTK_GDF_125_Watt' : 0x585,
|
||||
'OVTK_GDF_150_Watt' : 0x586,
|
||||
'OVTK_GDF_175_Watt' : 0x587,
|
||||
'OVTK_GDF_200_Watt' : 0x588,
|
||||
'OVTK_GDF_225_Watt' : 0x589,
|
||||
'OVTK_GDF_250_Watt' : 0x58A,
|
||||
'OVTK_GDF_275_Watt' : 0x58B,
|
||||
'OVTK_GDF_300_Watt' : 0x58C,
|
||||
'OVTK_GDF_325_Watt' : 0x58D,
|
||||
'OVTK_GDF_350_Watt' : 0x58E,
|
||||
|
||||
'OVTK_GDF_Start_Of_New_Segment' : 0x7FFE,
|
||||
'OVTK_GDF_Non_Equidistant_Sampling_Value' : 0x7FFF
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#File Name : openvibe.py
|
||||
#Created By : Aurelien Van Langhenhove
|
||||
|
||||
from StimulationsCodes import *
|
||||
import sys, traceback, collections
|
||||
from io import StringIO
|
||||
|
||||
class NewStd(StringIO):
|
||||
def __init__(self):
|
||||
StringIO.__init__(self)
|
||||
def flush(self):
|
||||
self.truncate(0)
|
||||
self.seek(0)
|
||||
def isempty(self):
|
||||
if len(self.len) == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
sys.stdout = NewStd()
|
||||
sys.stderr = NewStd()
|
||||
|
||||
|
||||
def execfileHandlingException(filename, maindictionary):
|
||||
print("executing script file")
|
||||
|
||||
try:
|
||||
maindictionary['box'] = OVBox(default=True) # on en cree une au cas ou
|
||||
#print(globals())
|
||||
exec(compile(open(filename, "rb").read(), filename, 'exec'), maindictionary)
|
||||
return 0
|
||||
except:
|
||||
print("error")
|
||||
print(traceback.format_exc())
|
||||
return -1
|
||||
|
||||
def decoratorFunction(target):
|
||||
""" add a try except block to protect openvibe box in case of exception """
|
||||
def wrapper(self):
|
||||
try :
|
||||
print("using decorator")
|
||||
print('Calling function "%s"' % target.__name__)
|
||||
return target(self)
|
||||
except:
|
||||
print(traceback.format_exc())
|
||||
|
||||
return wrapper
|
||||
|
||||
class OVChunk(object):
|
||||
def __init__(self, startTime, endTime):
|
||||
self.startTime = startTime
|
||||
self.endTime = endTime
|
||||
|
||||
|
||||
class OVStreamedMatrixHeader(OVChunk):
|
||||
def __init__(self, startTime, endTime, dimensionSizes, dimensionLabels):
|
||||
OVChunk.__init__(self, startTime, endTime)
|
||||
self.dimensionSizes = list(dimensionSizes)
|
||||
self.dimensionLabels = list(dimensionLabels)
|
||||
|
||||
def getDimensionCount(self):
|
||||
return len(self.dimensionSizes)
|
||||
|
||||
def getBufferElementCount(self):
|
||||
elementCount = 0
|
||||
for dimension, size in enumerate(self.dimensionSizes):
|
||||
if dimension == 0:
|
||||
elementCount = int(size)
|
||||
else:
|
||||
elementCount *= int(size)
|
||||
return elementCount
|
||||
|
||||
class OVStreamedMatrixBuffer(OVChunk, list):
|
||||
def __init__(self, startTime, endTime, bufferElements):
|
||||
OVChunk.__init__(self, startTime, endTime)
|
||||
list.__init__(self, bufferElements)
|
||||
|
||||
class OVStreamedMatrixEnd(OVChunk):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class OVSignalHeader(OVStreamedMatrixHeader):
|
||||
def __init__(self, startTime, endTime, dimensionSizes, dimensionLabels, samplingRate):
|
||||
OVStreamedMatrixHeader.__init__(self, startTime, endTime, dimensionSizes, dimensionLabels)
|
||||
self.samplingRate = int(samplingRate)
|
||||
|
||||
class OVSignalBuffer(OVStreamedMatrixBuffer):
|
||||
pass
|
||||
|
||||
class OVSignalEnd(OVChunk):
|
||||
pass
|
||||
|
||||
|
||||
class OVStimulation(object):
|
||||
def __init__(self, identifier, date, duration):
|
||||
self.identifier = identifier
|
||||
self.date = date
|
||||
self.duration = duration
|
||||
|
||||
class OVStimulationHeader(OVChunk):
|
||||
pass
|
||||
|
||||
class OVStimulationSet(OVChunk, list):
|
||||
def __init__(self, startTime, endTime):
|
||||
list.__init__(self)
|
||||
OVChunk.__init__(self, startTime, endTime)
|
||||
|
||||
def append(self, item):
|
||||
if isinstance(item, OVStimulation):
|
||||
list.append(self, item)
|
||||
else:
|
||||
raise TypeError("The item must be an OVStimulation")
|
||||
|
||||
class OVStimulationEnd(OVChunk):
|
||||
pass
|
||||
|
||||
|
||||
class OVBuffer(object):
|
||||
def __init__(self, inputType):
|
||||
self.__deque = collections.deque()
|
||||
self.__type = inputType
|
||||
def __len__(self):
|
||||
return len(self.__deque)
|
||||
def __getitem__(self, key):
|
||||
return self.__deque[key]
|
||||
def __setitem__(self, key, item):
|
||||
self.__deque[key] = item
|
||||
def __delitem__(self, key):
|
||||
del self.__deque[key]
|
||||
def append(self, toAppend):
|
||||
self.__deque.appendleft(toAppend)
|
||||
def pop(self):
|
||||
return self.__deque.pop()
|
||||
def type(self):
|
||||
return self.__type
|
||||
|
||||
|
||||
class OVBox(object):
|
||||
def __init__(self, default=False):
|
||||
self.input = list()
|
||||
self.output = list()
|
||||
self.setting = dict()
|
||||
self.var = dict()
|
||||
self._clock = 0
|
||||
self._currentTime = 0.
|
||||
self.default= default
|
||||
def addInput(self, inputType):
|
||||
self.input.append(OVBuffer(inputType))
|
||||
def addOutput(self, outputType):
|
||||
self.output.append(OVBuffer(outputType))
|
||||
def getClock(self):
|
||||
return self._clock
|
||||
def getCurrentTime(self):
|
||||
return self._currentTime
|
||||
def initialize(self):
|
||||
if self.default == True:
|
||||
print("The box instance has not been created by user script, using default one from openvibe.py (dummy box).")
|
||||
pass
|
||||
def process(self):
|
||||
pass
|
||||
def uninitialize(self):
|
||||
pass
|
||||
def realInitialize(self):
|
||||
try :
|
||||
self.initialize()
|
||||
except:
|
||||
print(traceback.format_exc())
|
||||
def realProcess(self):
|
||||
try :
|
||||
self.process()
|
||||
except:
|
||||
print(traceback.format_exc())
|
||||
def realUninitialize(self):
|
||||
try :
|
||||
self.uninitialize()
|
||||
except:
|
||||
print(traceback.format_exc())
|
||||
+1613
File diff suppressed because it is too large
Load Diff
+143
@@ -0,0 +1,143 @@
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyPython3
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 3)
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
//#include <map>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Python {
|
||||
class CBoxAlgorithmPython3 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 processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_Python3)
|
||||
|
||||
protected:
|
||||
|
||||
uint64_t m_clockFrequency = 0;
|
||||
CString m_scriptFilename;
|
||||
|
||||
std::vector<Toolkit::TDecoder<CBoxAlgorithmPython3>*> m_decoders;
|
||||
std::vector<Toolkit::TEncoder<CBoxAlgorithmPython3>*> m_encoders;
|
||||
|
||||
//std::map<char,PyObject *> m_PyObjectMap;
|
||||
PyObject *m_box = nullptr, *m_boxInput = nullptr, *m_boxOutput = nullptr, *m_boxSetting = nullptr,
|
||||
*m_boxCurrentTime = nullptr, *m_boxInitialize = nullptr, *m_boxProcess = nullptr, *m_boxUninitialize = nullptr;
|
||||
bool m_initializeSucceeded = false;
|
||||
|
||||
|
||||
bool logSysStd(const bool out);
|
||||
bool logSysStdout() { return logSysStd(true); }
|
||||
bool logSysStderr() { return logSysStd(false); }
|
||||
void buildPythonSettings();
|
||||
|
||||
bool initializePythonSafely();
|
||||
//bool clearPyObjectMap();
|
||||
bool transferStreamedMatrixInputChunksToPython(const size_t index);
|
||||
bool transferStreamedMatrixOutputChunksFromPython(const size_t index);
|
||||
bool transferSignalInputChunksToPython(const size_t index);
|
||||
bool transferSignalOutputChunksFromPython(const size_t index);
|
||||
bool transferStimulationInputChunksToPython(const size_t index);
|
||||
bool transferStimulationOutputChunksFromPython(const size_t index);
|
||||
|
||||
static bool m_isPythonInitialized;
|
||||
|
||||
// These are all borrowed references in python v3.7. Do not free them.
|
||||
static PyObject *m_mainModule, *m_mainDictionnary;
|
||||
static PyObject *m_matrixHeader, *m_matrixBuffer, *m_matrixEnd;
|
||||
static PyObject *m_signalHeader, *m_signalBuffer, *m_signalEnd;
|
||||
static PyObject *m_stimulationHeader, *m_stimulation, *m_stimulationSet, *m_stimulationEnd;
|
||||
static PyObject* m_buffer;
|
||||
static PyObject* m_execFileFunction;
|
||||
static PyObject *m_stdout, *m_stderr;
|
||||
};
|
||||
|
||||
class CBoxAlgorithmPython3Listener final : public Toolkit::TBoxListener<IBoxListener>
|
||||
{
|
||||
public:
|
||||
bool onInputAdded(Kernel::IBox& box, const size_t index) override
|
||||
{
|
||||
box.setInputType(index, OV_TypeId_StreamedMatrix);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool onOutputAdded(Kernel::IBox& box, const size_t index) override
|
||||
{
|
||||
box.setOutputType(index, OV_TypeId_StreamedMatrix);
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
|
||||
};
|
||||
|
||||
class CBoxAlgorithmPython3Desc final : virtual public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Python 3 scripting"); }
|
||||
CString getAuthorName() const override { return CString("Aurelien Van Langhenhove and Laurent George"); }
|
||||
CString getAuthorCompanyName() const override { return CString("CICIT Garches, Inria"); }
|
||||
CString getShortDescription() const override { return CString("This box executes a python script."); }
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString("Scripting"); }
|
||||
CString getVersion() const override { return CString("0.1"); }
|
||||
CString getStockItemName() const override { return CString("gtk-convert"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_Python3; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmPython3; }
|
||||
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmPython3Listener; }
|
||||
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addSetting("Clock frequency (Hz)", OV_TypeId_Integer, "64");
|
||||
prototype.addSetting("Script", OV_TypeId_Script, "");
|
||||
|
||||
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.addInputSupport(OV_TypeId_Signal);
|
||||
prototype.addInputSupport(OV_TypeId_Stimulations);
|
||||
prototype.addInputSupport(OV_TypeId_StreamedMatrix);
|
||||
|
||||
prototype.addOutputSupport(OV_TypeId_Signal);
|
||||
prototype.addOutputSupport(OV_TypeId_Stimulations);
|
||||
prototype.addOutputSupport(OV_TypeId_StreamedMatrix);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_Python3Desc)
|
||||
};
|
||||
} // namespace Python
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // #if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 3)
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyPython3
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
// Boxes
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#define OVP_ClassId_BoxAlgorithm_Python3 OpenViBE::CIdentifier(0x5DC4F669, 0xD3FD4D63)
|
||||
#define OVP_ClassId_BoxAlgorithm_Python3Desc OpenViBE::CIdentifier(0x404B6FFD, 0x12BDD423)
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
#if defined TARGET_HAS_ThirdPartyPython3 && !(defined(WIN32) && defined(TARGET_BUILDTYPE_Debug))
|
||||
// Windows debug build doesn't typically link as most people don't have the python debug library.
|
||||
|
||||
#include "box-algorithms/ovpCBoxAlgorithmPython3.h"
|
||||
|
||||
#if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 3)
|
||||
|
||||
#ifdef TARGET_OS_Windows
|
||||
#include "windows.h"
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
class CPython3Initializer
|
||||
{
|
||||
public:
|
||||
CPython3Initializer();
|
||||
~CPython3Initializer();
|
||||
bool isPython3Available() const { return m_pythonAvailable; }
|
||||
private:
|
||||
#ifdef TARGET_OS_Windows
|
||||
static bool checkPython3Path();
|
||||
#endif
|
||||
// PyThreadState *m_pMainPyThreadState;
|
||||
bool m_pythonAvailable;
|
||||
};
|
||||
|
||||
#ifdef TARGET_OS_Windows
|
||||
bool CPython3Initializer::checkPython3Path()
|
||||
{
|
||||
std::wstring ws(Py_GetPath());
|
||||
std::string path(ws.begin(), ws.end());
|
||||
|
||||
size_t found = path.find_first_of(';');
|
||||
while (found != std::string::npos)
|
||||
{
|
||||
if (found > 0)
|
||||
{
|
||||
std::string filename = path.substr(0, found);
|
||||
const bool exists = (_access(filename.c_str(), 0) == 0);
|
||||
if (exists) { return true; }
|
||||
}
|
||||
path = path.substr(found + 1);
|
||||
found = path.find_first_of(';');
|
||||
}
|
||||
|
||||
std::cout << "Python directory not found. You probably have a corrupted python installation!" << std::endl;
|
||||
std::cout << "The tried path from Py_GetPath() was [" << Py_GetPath() << "]\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
CPython3Initializer::CPython3Initializer() : m_pythonAvailable(false)
|
||||
{
|
||||
#ifdef TARGET_OS_Windows
|
||||
__try
|
||||
{
|
||||
// We do not care about the last file, since it is the OpenViBE runtime path
|
||||
if (!Py_IsInitialized() && checkPython3Path())
|
||||
{
|
||||
Py_Initialize();
|
||||
m_pythonAvailable = true;
|
||||
}
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER) { }
|
||||
#else
|
||||
if (!Py_IsInitialized())
|
||||
{
|
||||
Py_Initialize();
|
||||
m_pythonAvailable = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
CPython3Initializer::~CPython3Initializer()
|
||||
{
|
||||
if (m_pythonAvailable)
|
||||
{
|
||||
m_pythonAvailable = false;
|
||||
Py_Finalize();
|
||||
}
|
||||
}
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Python {
|
||||
|
||||
OVP_Declare_Begin()
|
||||
static CPython3Initializer python3Init;
|
||||
if (python3Init.isPython3Available()) { OVP_Declare_New(CBoxAlgorithmPython3Desc); }
|
||||
OVP_Declare_End()
|
||||
|
||||
} // namespace Python
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
//-------------------------------------------------------------------------------------------------
|
||||
|
||||
#else
|
||||
#pragma message ("WARNING: Python 3.x headers are required to build the Python plugin, different includes found, skipped")
|
||||
#endif // #if defined(PY_MAJOR_VERSION) && (PY_MAJOR_VERSION == 2)
|
||||
#endif // TARGET_HAS_ThirdPartyPython3
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
PROJECT(openvibe-plugins-contrib-signal-processing)
|
||||
|
||||
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
|
||||
SET(PROJECT_VERSION ${OV_GLOBAL_VERSION_STRING})
|
||||
|
||||
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.inl)
|
||||
|
||||
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
|
||||
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
FOLDER ${PLUGINS_FOLDER}
|
||||
COMPILE_FLAGS "-DOVP_Exports -DOVP_Shared")
|
||||
|
||||
# -----------------------------
|
||||
INCLUDE("FindOpenViBE")
|
||||
INCLUDE("FindOpenViBECommon")
|
||||
INCLUDE("FindOpenViBEToolkit")
|
||||
INCLUDE("FindOpenViBEModuleEBML")
|
||||
# INCLUDE("FindThirdPartyBoost")
|
||||
INCLUDE("FindThirdPartyITPP")
|
||||
|
||||
# -----------------------------
|
||||
# Install files
|
||||
# -----------------------------
|
||||
INSTALL(TARGETS ${PROJECT_NAME}
|
||||
RUNTIME DESTINATION ${DIST_BINDIR}
|
||||
LIBRARY DESTINATION ${DIST_LIBDIR}
|
||||
ARCHIVE DESTINATION ${DIST_LIBDIR})
|
||||
|
||||
INSTALL(DIRECTORY box-tutorials DESTINATION ${DIST_DATADIR}/openvibe/scenarios/)
|
||||
INSTALL(DIRECTORY metaboxes DESTINATION ${DIST_DATADIR}/openvibe/)
|
||||
+790
@@ -0,0 +1,790 @@
|
||||
<OpenViBE-Scenario>
|
||||
<FormatVersion>1</FormatVersion>
|
||||
<Creator>OpenVIBE</Creator>
|
||||
<CreatorVersion>2.0</CreatorVersion>
|
||||
<Settings></Settings>
|
||||
<Inputs></Inputs>
|
||||
<Outputs></Outputs>
|
||||
<Boxes>
|
||||
<Box>
|
||||
<Identifier>(0x000002df, 0x00004ca1)</Identifier>
|
||||
<Name>Reconstructed</Name>
|
||||
<AlgorithmClassIdentifier>(0x0055be5f, 0x087bdd12)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Data</Name>
|
||||
</Input>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Stimulations</Name>
|
||||
</Input>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6ab26b81, 0x0f8c02f3)</TypeIdentifier>
|
||||
<Name>Channel Units</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x5de046a6, 0x086340aa)</TypeIdentifier>
|
||||
<Name>Display Mode</Name>
|
||||
<DefaultValue>Scan</DefaultValue>
|
||||
<Value>Scan</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x33a30739, 0x00d5299b)</TypeIdentifier>
|
||||
<Name>Auto vertical scale</Name>
|
||||
<DefaultValue>Per channel</DefaultValue>
|
||||
<Value>Per channel</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Scale refresh interval (secs)</Name>
|
||||
<DefaultValue>5</DefaultValue>
|
||||
<Value>5</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Vertical Scale</Name>
|
||||
<DefaultValue>100</DefaultValue>
|
||||
<Value>100</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Vertical Offset</Name>
|
||||
<DefaultValue>0</DefaultValue>
|
||||
<Value>0</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Time Scale</Name>
|
||||
<DefaultValue>10</DefaultValue>
|
||||
<Value>10</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Horizontal ruler</Name>
|
||||
<DefaultValue>true</DefaultValue>
|
||||
<Value>true</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Vertical ruler</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>false</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Multiview</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>false</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>560.000000</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>800.000000</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x92c056a7, 0x2dc71aff)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
|
||||
<Value>false</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>9</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>3</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x000005e4, 0x00006597)</Identifier>
|
||||
<Name>Amplitude</Name>
|
||||
<AlgorithmClassIdentifier>(0x7b0ddb65, 0xfdc51488)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x1f261c0a, 0x593bf6bd)</TypeIdentifier>
|
||||
<Name>Matrix</Name>
|
||||
</Input>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Markers</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
|
||||
<Name>Channel Localisation</Name>
|
||||
<DefaultValue>${AdvancedViz_ChannelLocalisation}</DefaultValue>
|
||||
<Value>${AdvancedViz_ChannelLocalisation}</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x8f02e3f6, 0xffb00f4b)</TypeIdentifier>
|
||||
<Name>Temporal Coherence</Name>
|
||||
<DefaultValue>Time Locked</DefaultValue>
|
||||
<Value>Time Locked</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Time Scale</Name>
|
||||
<DefaultValue>20</DefaultValue>
|
||||
<Value>20</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Matrix Count</Name>
|
||||
<DefaultValue>50</DefaultValue>
|
||||
<Value>50</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Gain</Name>
|
||||
<DefaultValue>1</DefaultValue>
|
||||
<Value>0.1</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
|
||||
<Name>Caption</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value></Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x3d3c7c7f, 0xef0e7129)</TypeIdentifier>
|
||||
<Name>Color</Name>
|
||||
<DefaultValue>${AdvancedViz_DefaultColorGradient}</DefaultValue>
|
||||
<Value>${AdvancedViz_DefaultColorGradient}</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>400</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>560</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0xafb1edd5, 0x68e1da05)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>7</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x000009d1, 0x00005baa)</Identifier>
|
||||
<Name>Sinus oscillator</Name>
|
||||
<AlgorithmClassIdentifier>(0x7e33bdb8, 0x68194a4a)</AlgorithmClassIdentifier>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Generated signal</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Channel count</Name>
|
||||
<DefaultValue>4</DefaultValue>
|
||||
<Value>1</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Sampling frequency</Name>
|
||||
<DefaultValue>512</DefaultValue>
|
||||
<Value>256</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Generated epoch sample count</Name>
|
||||
<DefaultValue>32</DefaultValue>
|
||||
<Value>128</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>160.000000</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>768.000000</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x0b214ed8, 0x1f9ad83a)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
|
||||
<Value>(0x00000000, 0x013a1c38)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
|
||||
<Value>false</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>3</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00000d81, 0x00006768)</Identifier>
|
||||
<Name>IFFT box</Name>
|
||||
<AlgorithmClassIdentifier>(0xd533e997, 0x4afd2423)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x1f261c0a, 0x593bf6bd)</TypeIdentifier>
|
||||
<Name>real part</Name>
|
||||
</Input>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x1f261c0a, 0x593bf6bd)</TypeIdentifier>
|
||||
<Name>imaginary part</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Signal output</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>464.000000</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>784.000000</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x9e5c01e9, 0xe6051e24)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
|
||||
<Value>(0x00000000, 0x378ea7ca)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
|
||||
<Value>false</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00002fc6, 0x000073a3)</Identifier>
|
||||
<Name>Phase</Name>
|
||||
<AlgorithmClassIdentifier>(0x7b0ddb65, 0xfdc51488)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x1f261c0a, 0x593bf6bd)</TypeIdentifier>
|
||||
<Name>Matrix</Name>
|
||||
</Input>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Markers</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
|
||||
<Name>Channel Localisation</Name>
|
||||
<DefaultValue>${AdvancedViz_ChannelLocalisation}</DefaultValue>
|
||||
<Value>${AdvancedViz_ChannelLocalisation}</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x8f02e3f6, 0xffb00f4b)</TypeIdentifier>
|
||||
<Name>Temporal Coherence</Name>
|
||||
<DefaultValue>Time Locked</DefaultValue>
|
||||
<Value>Time Locked</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Time Scale</Name>
|
||||
<DefaultValue>20</DefaultValue>
|
||||
<Value>20</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Matrix Count</Name>
|
||||
<DefaultValue>50</DefaultValue>
|
||||
<Value>50</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Gain</Name>
|
||||
<DefaultValue>1</DefaultValue>
|
||||
<Value>1</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
|
||||
<Name>Caption</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value></Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x3d3c7c7f, 0xef0e7129)</TypeIdentifier>
|
||||
<Name>Color</Name>
|
||||
<DefaultValue>${AdvancedViz_DefaultColorGradient}</DefaultValue>
|
||||
<Value>${AdvancedViz_DefaultColorGradient}</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>400</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>688</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0xafb1edd5, 0x68e1da05)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>7</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00004b02, 0x00001fdd)</Identifier>
|
||||
<Name>Spectral analysis</Name>
|
||||
<AlgorithmClassIdentifier>(0x84218ff8, 0xa87e7995)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Input signal</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x1f261c0a, 0x593bf6bd)</TypeIdentifier>
|
||||
<Name>Amplitude</Name>
|
||||
</Output>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x1f261c0a, 0x593bf6bd)</TypeIdentifier>
|
||||
<Name>Phase</Name>
|
||||
</Output>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x1f261c0a, 0x593bf6bd)</TypeIdentifier>
|
||||
<Name>Real Part</Name>
|
||||
</Output>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x1f261c0a, 0x593bf6bd)</TypeIdentifier>
|
||||
<Name>Imag Part</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Amplitude</Name>
|
||||
<DefaultValue>true</DefaultValue>
|
||||
<Value>true</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Phase</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>true</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Real Part</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>true</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Imaginary Part</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>true</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>256.000000</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>768.000000</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x4e5c55a0, 0xe69eb7db)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
|
||||
<Value>(0x00000000, 0x3a916194)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
|
||||
<Value>false</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
|
||||
<Value>4</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x000051d7, 0x00000db5)</Identifier>
|
||||
<Name>Original</Name>
|
||||
<AlgorithmClassIdentifier>(0x0055be5f, 0x087bdd12)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Data</Name>
|
||||
</Input>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Stimulations</Name>
|
||||
</Input>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6ab26b81, 0x0f8c02f3)</TypeIdentifier>
|
||||
<Name>Channel Units</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x5de046a6, 0x086340aa)</TypeIdentifier>
|
||||
<Name>Display Mode</Name>
|
||||
<DefaultValue>Scan</DefaultValue>
|
||||
<Value>Scan</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x33a30739, 0x00d5299b)</TypeIdentifier>
|
||||
<Name>Auto vertical scale</Name>
|
||||
<DefaultValue>Per channel</DefaultValue>
|
||||
<Value>Per channel</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Scale refresh interval (secs)</Name>
|
||||
<DefaultValue>5</DefaultValue>
|
||||
<Value>5</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Vertical Scale</Name>
|
||||
<DefaultValue>100</DefaultValue>
|
||||
<Value>100</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Vertical Offset</Name>
|
||||
<DefaultValue>0</DefaultValue>
|
||||
<Value>0</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Time Scale</Name>
|
||||
<DefaultValue>10</DefaultValue>
|
||||
<Value>10</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Horizontal ruler</Name>
|
||||
<DefaultValue>true</DefaultValue>
|
||||
<Value>true</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Vertical ruler</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>false</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Multiview</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>false</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>256.000000</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>576.000000</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x92c056a7, 0x2dc71aff)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
|
||||
<Value>false</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>9</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>3</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
</Boxes>
|
||||
<Links>
|
||||
<Link>
|
||||
<Identifier>(0x000011ae, 0x00002344)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00004b02, 0x00001fdd)</BoxIdentifier>
|
||||
<BoxOutputIndex>1</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00002fc6, 0x000073a3)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00002717, 0x00002653)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00004b02, 0x00001fdd)</BoxIdentifier>
|
||||
<BoxOutputIndex>2</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00000d81, 0x00006768)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00003b98, 0x00007502)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00004b02, 0x00001fdd)</BoxIdentifier>
|
||||
<BoxOutputIndex>3</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00000d81, 0x00006768)</BoxIdentifier>
|
||||
<BoxInputIndex>1</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x0000501d, 0x0000052c)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x000009d1, 0x00005baa)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00004b02, 0x00001fdd)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x000063c7, 0x00005015)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00000d81, 0x00006768)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x000002df, 0x00004ca1)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x0000688a, 0x00001068)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x000009d1, 0x00005baa)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x000051d7, 0x00000db5)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00006c16, 0x00004c37)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00004b02, 0x00001fdd)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x000005e4, 0x00006597)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
</Links>
|
||||
<Comments>
|
||||
<Comment>
|
||||
<Identifier>(0x00002920, 0x00007fea)</Identifier>
|
||||
<Text>The reconstructed signal should
|
||||
equal the original one
|
||||
</Text>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x473d9a43, 0x97fc0a97)</Identifier>
|
||||
<Value>1104.000000</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x7234b86b, 0x2b8651a5)</Identifier>
|
||||
<Value>498.000000</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Comment>
|
||||
<Comment>
|
||||
<Identifier>(0x00006544, 0x00006793)</Identifier>
|
||||
<Text>A signal is decomposed by Fast Fourier Transform</Text>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x473d9a43, 0x97fc0a97)</Identifier>
|
||||
<Value>1104.000000</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x7234b86b, 0x2b8651a5)</Identifier>
|
||||
<Value>256.000000</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Comment>
|
||||
<Comment>
|
||||
<Identifier>(0x00006c55, 0x00000be7)</Identifier>
|
||||
<Text>At this stage the signals are in Fourier space
|
||||
transmitted in the Spectrum stream format using
|
||||
one stream for real and one for imaginary part</Text>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x473d9a43, 0x97fc0a97)</Identifier>
|
||||
<Value>1104.000000</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x7234b86b, 0x2b8651a5)</Identifier>
|
||||
<Value>377.000000</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Comment>
|
||||
</Comments>
|
||||
<Metadata>
|
||||
<Entry>
|
||||
<Identifier>(0x0000775c, 0x000078ff)</Identifier>
|
||||
<Type>(0x3bcce5d2, 0x43f2d968)</Type>
|
||||
<Data>[{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"height":320,"identifier":"(0x00003f5d, 0x000063e0)","name":"Default window","parentIdentifier":"(0xffffffff, 0xffffffff)","type":1,"width":475},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"identifier":"(0x0000498b, 0x00003981)","index":0,"name":"Default tab","parentIdentifier":"(0x00003f5d, 0x000063e0)","type":2},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":2,"dividerPosition":228,"identifier":"(0x00007431, 0x00007d23)","index":0,"maxDividerPosition":275,"name":"Vertical split","parentIdentifier":"(0x0000498b, 0x00003981)","type":4},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":2,"dividerPosition":158,"identifier":"(0x000025c1, 0x00006774)","index":0,"maxDividerPosition":212,"name":"Vertical split","parentIdentifier":"(0x00007431, 0x00007d23)","type":4},{"boxIdentifier":"(0x000002df, 0x00004ca1)","childCount":0,"identifier":"(0x0000293c, 0x000050be)","index":1,"parentIdentifier":"(0x00007431, 0x00007d23)","type":3},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":2,"dividerPosition":69,"identifier":"(0x00007b2e, 0x00005e1f)","index":0,"maxDividerPosition":142,"name":"Vertical split","parentIdentifier":"(0x000025c1, 0x00006774)","type":4},{"boxIdentifier":"(0x00002fc6, 0x000073a3)","childCount":0,"identifier":"(0x00001fb9, 0x00004ce1)","index":1,"parentIdentifier":"(0x000025c1, 0x00006774)","type":3},{"boxIdentifier":"(0x000051d7, 0x00000db5)","childCount":0,"identifier":"(0x00007d91, 0x0000288c)","index":0,"parentIdentifier":"(0x00007b2e, 0x00005e1f)","type":3},{"boxIdentifier":"(0x000005e4, 0x00006597)","childCount":0,"identifier":"(0x00001fb4, 0x0000332d)","index":1,"parentIdentifier":"(0x00007b2e, 0x00005e1f)","type":3}]</Data>
|
||||
</Entry>
|
||||
</Metadata>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x790d75b8, 0x3bb90c33)</Identifier>
|
||||
<Value>Jussi T. Lindgren / Inria</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x8c1fc55b, 0x7b433dc2)</Identifier>
|
||||
<Value>1.0</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x9f5c4075, 0x4a0d3666)</Identifier>
|
||||
<Value>FFT Decomposition</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf36a1567, 0xd13c53da)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf6b2e3fa, 0x7bd43926)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf8034a49, 0x8b3f37cc)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</OpenViBE-Scenario>
|
||||
+1205
File diff suppressed because it is too large
Load Diff
+1241
File diff suppressed because it is too large
Load Diff
+93
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* \page BoxAlgorithm_CSPSpatialFilterTrainer CSP Spatial Filter Trainer
|
||||
__________________________________________________________________
|
||||
|
||||
Detailed description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_CSPSpatialFilterTrainer_Description|
|
||||
|
||||
This box computes spatial filters according to the Common Spatial Pattern algorithm. The goal of the algorithm is to improve the discrimination of two types of signals.
|
||||
The spatial filters are constructed in a way they maximize the variance for signals of the first condition while at the same time they minimize it for the second condition.
|
||||
This can be used for discriminating the signals of two commonly used motor-imagery tasks (e.g. left versus right hand movement).
|
||||
It can also be used for two-class SSVEP experiments or any other experiment where the discriminative information is contained in the variance (or power in a certain band) of the signal conditions.
|
||||
|
||||
Please note that this implementation computes a <b>trace normalization</b>.
|
||||
|
||||
* |OVP_DocEnd_BoxAlgorithm_CSPSpatialFilterTrainer_Description|
|
||||
__________________________________________________________________
|
||||
|
||||
Inputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_CSPSpatialFilterTrainer_Inputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_CSPSpatialFilterTrainer_Inputs|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_CSPSpatialFilterTrainer_Input1|
|
||||
This stimulus input is needed to indicate the end of a recording session (or end of file). It then triggers the training/computation of the CSP filters.
|
||||
* |OVP_DocEnd_BoxAlgorithm_CSPSpatialFilterTrainer_Input1|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_CSPSpatialFilterTrainer_Input2|
|
||||
This input expects epoched data for the first condition (e.g. epochs for left hand motor imagery).
|
||||
* |OVP_DocEnd_BoxAlgorithm_CSPSpatialFilterTrainer_Input2|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_CSPSpatialFilterTrainer_Input3|
|
||||
This input expects epoched data for the second condition (e.g. epochs for right hand motor imagery).
|
||||
* |OVP_DocEnd_BoxAlgorithm_CSPSpatialFilterTrainer_Input3|
|
||||
__________________________________________________________________
|
||||
|
||||
Outputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_CSPSpatialFilterTrainer_Outputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_CSPSpatialFilterTrainer_Outputs|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_CSPSpatialFilterTrainer_Output1|
|
||||
The CSP Trainer outputs the stimulation <b>OVTK_StimulationId_TrainCompleted</b> when the training process was successful. No output is produced if the process failed.
|
||||
* |OVP_DocEnd_BoxAlgorithm_CSPSpatialFilterTrainer_Output1|
|
||||
__________________________________________________________________
|
||||
|
||||
Settings description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_CSPSpatialFilterTrainer_Settings|
|
||||
|
||||
* |OVP_DocEnd_BoxAlgorithm_CSPSpatialFilterTrainer_Settings|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_CSPSpatialFilterTrainer_Setting1|
|
||||
This should contain the stimulus identifier denoting the end of a recording session or end of file, e.g. OVTK_GDF_End_Of_Session or OVTK_StimulationId_ExperimentStop.
|
||||
* |OVP_DocEnd_BoxAlgorithm_CSPSpatialFilterTrainer_Setting1|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_CSPSpatialFilterTrainer_Setting2|
|
||||
This setting contains the path and filename of the configuration file in which the computed spatial filters are saved.
|
||||
* |OVP_DocEnd_BoxAlgorithm_CSPSpatialFilterTrainer_Setting2|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_CSPSpatialFilterTrainer_Setting3|
|
||||
Here you need to determine how many spatial filters will be computed (default value is two).
|
||||
* |OVP_DocEnd_BoxAlgorithm_CSPSpatialFilterTrainer_Setting3|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_CSPSpatialFilterTrainer_Setting4|
|
||||
If true, the output file will be a box configuration XML. Otherwise it will be a text format matrix.
|
||||
* |OVP_DocEnd_BoxAlgorithm_CSPSpatialFilterTrainer_Setting4|
|
||||
__________________________________________________________________
|
||||
|
||||
Examples description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_CSPSpatialFilterTrainer_Examples|
|
||||
Before training the spatial filter you should first filter the data with respect to the desired band (e.g. for motor imagery, certain people display good results in a narrow pass-band of 8-12Hz, others in 8-30Hz).
|
||||
As in the example scenario below, one could also opt for different pass-bands and compute filters in each of them, finally letting the subsequently trained classifier decide which features are important.
|
||||
|
||||
\image html csp_training.png "Example scenario to compute CSP filters"
|
||||
|
||||
Once the spatial filters are computed and saved in the configuration file, you will need to load it into the \ref Doc_BoxAlgorithm_SpatialFilter "Spatial Filter" box.
|
||||
* |OVP_DocEnd_BoxAlgorithm_CSPSpatialFilterTrainer_Examples|
|
||||
__________________________________________________________________
|
||||
|
||||
Miscellaneous description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_CSPSpatialFilterTrainer_Miscellaneous|
|
||||
For the moment it is only implemented for two classes. Multiple classes can be supported in the future according to an all-versus-one scheme or through joint diagonalization.
|
||||
* |OVP_DocEnd_BoxAlgorithm_CSPSpatialFilterTrainer_Miscellaneous|
|
||||
*/
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* \page BoxAlgorithm_Downsampling Downsampling -
|
||||
__________________________________________________________________
|
||||
|
||||
Detailed description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Downsampling_Description|
|
||||
*
|
||||
* NOTE: This box has been deprecated. Please use
|
||||
* Signal Resampling box instead.
|
||||
*
|
||||
* This plugin is used to downsample the input signal. First, a
|
||||
* low-pass filter is applied to the input signal for anti-aliasing.
|
||||
* Then, the input signal is downsampled at the new sampling rate.
|
||||
* This plugin allows the selection of the kind of filter (Butterworth
|
||||
* or Chebyshev), the new sampling rate and the frequency
|
||||
* cutoff for the filter.
|
||||
*
|
||||
* |OVP_DocEnd_BoxAlgorithm_Downsampling_Description|
|
||||
__________________________________________________________________
|
||||
|
||||
Inputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Downsampling_Inputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_Downsampling_Inputs|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_Downsampling_Input1|
|
||||
* The input signal.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Downsampling_Input1|
|
||||
__________________________________________________________________
|
||||
|
||||
Outputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Downsampling_Outputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_Downsampling_Outputs|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_Downsampling_Output1|
|
||||
* |OVP_DocEnd_BoxAlgorithm_Downsampling_Output1|
|
||||
__________________________________________________________________
|
||||
|
||||
Settings description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Downsampling_Settings|
|
||||
* |OVP_DocEnd_BoxAlgorithm_Downsampling_Settings|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_Downsampling_Setting1|
|
||||
* New sampling rate in Hz chosen to downsample the input signal.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Downsampling_Setting1|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_Downsampling_Setting2|
|
||||
* Select the frequency cutoff of the low-pass filter as a ratio (1/2,
|
||||
* 1/3 or 1/4) of the new sampling rate.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Downsampling_Setting2|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_Downsampling_Setting3|
|
||||
* Select the kind of filter between Butterworth and Chebyshev.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Downsampling_Setting3|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_Downsampling_Setting4|
|
||||
* Order of the low-pass filter.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Downsampling_Setting4|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_Downsampling_Setting5|
|
||||
* If Chebyshev filter is selected, PassBand Ripple is a necessary info.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Downsampling_Setting5|
|
||||
__________________________________________________________________
|
||||
|
||||
Examples description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Downsampling_Examples|
|
||||
* Let's consider our input signal sampling rate is 1 kHz.
|
||||
* If the new selected sampling rate is 200 Hz and the Frequency
|
||||
* cutoff ratio is 1/4, then a low-pass filter (with frequency
|
||||
* cutoff equal to 200*1/4 = 50 Hz) is applied before downsampling
|
||||
* at 200 Hz.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Downsampling_Examples|
|
||||
__________________________________________________________________
|
||||
|
||||
Miscellaneous description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Downsampling_Miscellaneous|
|
||||
* This plugin downsamples the input signal and previously realizes
|
||||
* an anti-aliasing filtering.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Downsampling_Miscellaneous|
|
||||
*/
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* \page BoxAlgorithm_IndependentComponentAnalysisFastICA Independent Component Analysis (FastICA)
|
||||
__________________________________________________________________
|
||||
|
||||
Detailed description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Description|
|
||||
* This box attempts to find a decomposition of the signal to its
|
||||
* independent components. The approach is based on the FastICA algorithm.
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Description|
|
||||
__________________________________________________________________
|
||||
|
||||
Inputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Inputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Inputs|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Input1|
|
||||
* The input signal.
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Input1|
|
||||
__________________________________________________________________
|
||||
|
||||
Outputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Outputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Outputs|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Output1|
|
||||
* The decomposed signal.
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Output1|
|
||||
__________________________________________________________________
|
||||
|
||||
Settings description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Settings|
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Settings|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting1|
|
||||
* Number of independent components to extract (equals PCA dimension reduction)
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting1|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting2|
|
||||
* Which decomposition is desired?
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting2|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting3|
|
||||
* How many seconds of sample to collect to estimate the ICA model?
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting3|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting4|
|
||||
* Decomposition type. Deflation is an approach where each component is
|
||||
* estimated separately in turns. Symmetric estimation optimizes all
|
||||
* components at once.
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting4|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting5|
|
||||
* Maximum number of iterations
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting5|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting6|
|
||||
* Enable fine tuning?
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting6|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting7|
|
||||
* Maximum number of iterations for the fine tuning
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting7|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting8|
|
||||
* Used nonlinearity type
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting8|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting9|
|
||||
* Mu parameter
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting9|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting10|
|
||||
* Epsilon parameter
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting10|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting11|
|
||||
* Filename to save the estimated decomposition matrix W to
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting11|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting12|
|
||||
* Should the matrix W be saved to a file?
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Setting12|
|
||||
*
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Settings|
|
||||
|
||||
|
||||
__________________________________________________________________
|
||||
|
||||
Examples description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Examples|
|
||||
* One use-case of ICA is to attempt to separate the signal of interest from
|
||||
* nuisance artifacts. For example, supposing that ICA makes a meaningful decomposition
|
||||
* of your EEG signal, you will see artifacts such as those from eyeblinks more
|
||||
* clearly segregated to specific output channels instead of contaminating
|
||||
* all of the channels.
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Examples|
|
||||
__________________________________________________________________
|
||||
|
||||
Miscellaneous description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_IndependentComponentAnalysisFastICA_Miscellaneous|
|
||||
* This plugin applies the FastICA algorithm to the input signal. The box can store the
|
||||
* estimated decomposition matrix W to a file. This file can then be used later
|
||||
* in the spatial filter box to apply the decomposition on fresh data.
|
||||
*
|
||||
* The box also outputs the decomposed signal, but the decomposition is active only
|
||||
* after the model has been estimated (after the specified number of samples have been collected).
|
||||
* If you wish to decompose the whole data, then you can first train the ICA model, save the matrix,
|
||||
* and then separately apply it to the original data with the spatial filter.
|
||||
*
|
||||
* The FastICA algorithm is described in
|
||||
*
|
||||
* A. Hyvärinen. "Fast and Robust Fixed-Point Algorithms for Independent Component Analysis", IEEE Transactions on Neural Networks 10(3):626-634, 1999.
|
||||
*
|
||||
* The implementation used by the box is from the ITPP toolkit.
|
||||
*
|
||||
* |OVP_DocEnd_BoxAlgorithm_IndependentComponentAnalysisFastICA_Miscellaneous|
|
||||
*/
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* \page BoxAlgorithm_Min_MaxDetection Min/Max detection -
|
||||
__________________________________________________________________
|
||||
|
||||
Detailed description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Min_MaxDetection_Description|
|
||||
* This plugin is used to detect the minimum or the maximum value between
|
||||
* 2 dates. This plugin allows the selection of the minimum or the maximum
|
||||
* value to detect and the time start and time stop in between you are looking at.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Min_MaxDetection_Description|
|
||||
__________________________________________________________________
|
||||
|
||||
Inputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Min_MaxDetection_Inputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_Min_MaxDetection_Inputs|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_Min_MaxDetection_Input1|
|
||||
* The input signal.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Min_MaxDetection_Input1|
|
||||
__________________________________________________________________
|
||||
|
||||
Outputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Min_MaxDetection_Outputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_Min_MaxDetection_Outputs|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_Min_MaxDetection_Output1|
|
||||
* |OVP_DocEnd_BoxAlgorithm_Min_MaxDetection_Output1|
|
||||
__________________________________________________________________
|
||||
|
||||
Settings description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Min_MaxDetection_Settings|
|
||||
* |OVP_DocEnd_BoxAlgorithm_Min_MaxDetection_Settings|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_Min_MaxDetection_Setting1|
|
||||
* Select if you want to detect the Min or the Max.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Min_MaxDetection_Setting1|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_Min_MaxDetection_Setting2|
|
||||
* Starting time the algorithm will search the Min/Max value.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Min_MaxDetection_Setting2|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_Min_MaxDetection_Setting3|
|
||||
* Ending time the algorithm will search the Min/Max value.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Min_MaxDetection_Setting3|
|
||||
__________________________________________________________________
|
||||
|
||||
Examples description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Min_MaxDetection_Examples|
|
||||
* Let's consider our input signal is an ERP.
|
||||
* To detect the P300 ERP, select Max value and a Time Window Start equal
|
||||
* to 250 ms and a Time Window End equal to 450 ms.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Min_MaxDetection_Examples|
|
||||
__________________________________________________________________
|
||||
|
||||
Miscellaneous description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_Min_MaxDetection_Miscellaneous|
|
||||
* This plugin detects the minimum or the maximum value in a time window interval.
|
||||
* |OVP_DocEnd_BoxAlgorithm_Min_MaxDetection_Miscellaneous|
|
||||
*/
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* \page BoxAlgorithm_ModifiableTemporalFilter Modifiable Temporal filter -
|
||||
__________________________________________________________________
|
||||
|
||||
Detailed description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_ModifiableTemporalFilter_Description|
|
||||
* This plugin is used to filter the input signal. This plugin allows
|
||||
* the selection of the kind of filter (Butterworth or Chebyshev),
|
||||
* the kind of filter (low pass, high pass, band pass, band stop), the low
|
||||
* or/and the high passband edge and the passband ripple for the Chebyshev filter.
|
||||
* This box is a duplicate of the Temporal Filter box with all its settings marked as modifiable.
|
||||
* Each time a setting changes, the box recompute the filter coefficients accordingly.
|
||||
* |OVP_DocEnd_BoxAlgorithm_ModifiableTemporalFilter_Description|
|
||||
__________________________________________________________________
|
||||
|
||||
Inputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_ModifiableTemporalFilter_Inputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_ModifiableTemporalFilter_Inputs|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_ModifiableTemporalFilter_Input1|
|
||||
* The input signal.
|
||||
* |OVP_DocEnd_BoxAlgorithm_ModifiableTemporalFilter_Input1|
|
||||
__________________________________________________________________
|
||||
|
||||
Outputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_ModifiableTemporalFilter_Outputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_ModifiableTemporalFilter_Outputs|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_ModifiableTemporalFilter_Output1|
|
||||
* |OVP_DocEnd_BoxAlgorithm_ModifiableTemporalFilter_Output1|
|
||||
__________________________________________________________________
|
||||
|
||||
Settings description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_ModifiableTemporalFilter_Settings|
|
||||
* |OVP_DocEnd_BoxAlgorithm_ModifiableTemporalFilter_Settings|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_ModifiableTemporalFilter_Setting1|
|
||||
* Select the name of filter between Butterworth and Chebyshev. Modifiable online.
|
||||
* |OVP_DocEnd_BoxAlgorithm_ModifiableTemporalFilter_Setting1|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_ModifiableTemporalFilter_Setting2|
|
||||
* Select the kind of filter between Low pass, High pass, Band pass, Band stop. Modifiable online.
|
||||
* |OVP_DocEnd_BoxAlgorithm_ModifiableTemporalFilter_Setting2|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_ModifiableTemporalFilter_Setting3|
|
||||
* Order of the low-pass filter. Modifiable online.
|
||||
* |OVP_DocEnd_BoxAlgorithm_ModifiableTemporalFilter_Setting3|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_ModifiableTemporalFilter_Setting4|
|
||||
* Low edge for High pass, Band pass and Band stop filters. Modifiable online.
|
||||
* |OVP_DocEnd_BoxAlgorithm_ModifiableTemporalFilter_Setting4|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_ModifiableTemporalFilter_Setting5|
|
||||
* High edge for Low pass, Band pass and Band stop filters. Modifiable online.
|
||||
* |OVP_DocEnd_BoxAlgorithm_ModifiableTemporalFilter_Setting5|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_ModifiableTemporalFilter_Setting6|
|
||||
* If Chebyshev filter is selected, PassBand Ripple is a necessary info. Modifiable online.
|
||||
* |OVP_DocEnd_BoxAlgorithm_ModifiableTemporalFilter_Setting6|
|
||||
__________________________________________________________________
|
||||
|
||||
Examples description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_ModifiableTemporalFilter_Examples|
|
||||
* Let's consider our input signal is very noisy (50 Hz).
|
||||
* To filter this signal, select a Low pass Butterworth filter of 4th
|
||||
* order and High PassBand Edge equal to 30 Hz for example.
|
||||
* |OVP_DocEnd_BoxAlgorithm_ModifiableTemporalFilter_Examples|
|
||||
__________________________________________________________________
|
||||
|
||||
Miscellaneous description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_ModifiableTemporalFilter_Miscellaneous|
|
||||
* This plugin filters the input signal. Several filtering are available.
|
||||
* |OVP_DocEnd_BoxAlgorithm_ModifiableTemporalFilter_Miscellaneous|
|
||||
*/
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* \page BoxAlgorithm_SpectralAnalysisFFTINSERMContrib Spectral analysis -
|
||||
__________________________________________________________________
|
||||
|
||||
Detailed description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Description|
|
||||
The Spectral Analysis box performs spectrum computations on incoming signals and possible outputs include the spectrum amplitude (the power of the signal in a number of frequency bands), as well as its phase, real part and imaginary part. Output computations may be enabled/disabled from the settings dialog box. The analysis is performed using a <b> Fast Fourier Transform </b>.
|
||||
* |OVP_DocEnd_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Description|
|
||||
__________________________________________________________________
|
||||
|
||||
Inputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Inputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Inputs|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Input1|
|
||||
The input signal.
|
||||
* |OVP_DocEnd_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Input1|
|
||||
__________________________________________________________________
|
||||
|
||||
Outputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Outputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Outputs|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Output1|
|
||||
Amplitude of input signal in frequency bands.
|
||||
* |OVP_DocEnd_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Output1|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Output2|
|
||||
Phase of input signal
|
||||
* |OVP_DocEnd_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Output2|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Output3|
|
||||
Real part of input signal
|
||||
* |OVP_DocEnd_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Output3|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Output4|
|
||||
Imaginary part of input signal
|
||||
* |OVP_DocEnd_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Output4|
|
||||
__________________________________________________________________
|
||||
|
||||
Settings description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Settings|
|
||||
* |OVP_DocEnd_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Settings|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Setting1|
|
||||
* |OVP_DocEnd_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Setting1|
|
||||
__________________________________________________________________
|
||||
|
||||
Examples description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Examples|
|
||||
Practical example : visualizing the power spectrum of a signal.
|
||||
|
||||
Let's use a Signal Oscillator box to generator sinusoidal signals on one channel. Next we add a Spectral Analysis box and connect boxes together. We make sure the 'Amplitude' of the signal is computed by checking the appropriate setting in the settings dialog box (see image below). Finally, we connect the 'Amplitude' output connector of the Spectral Analysis box to the input connector of a Power Spectrum Display box. The player may now be launched to visualize the power spectrum of the signal.
|
||||
|
||||
\image html SpectralAnalysisFFTINSERMContrib_online.png "Visualizing the power spectrum of sinusoidal signals."
|
||||
* |OVP_DocEnd_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Examples|
|
||||
|
||||
__________________________________________________________________
|
||||
|
||||
Miscellaneous description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Miscellaneous|
|
||||
* |OVP_DocEnd_BoxAlgorithm_SpectralAnalysisFFTINSERMContrib_Miscellaneous|
|
||||
*/
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* \page BoxAlgorithm_TemporalFilterINSERMContrib Temporal filter -
|
||||
__________________________________________________________________
|
||||
|
||||
Detailed description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_TemporalFilterINSERMContrib_Description|
|
||||
* This plugin is used to filter the input signal. This plugin allows
|
||||
* the selection of the kind of filter (Butterworth or Chebyshev),
|
||||
* the kind of filter (low pass, high pass, band pass, band stop), the low
|
||||
* or/and the high passband edge and the passband ripple for the Chebyshev filter.
|
||||
* |OVP_DocEnd_BoxAlgorithm_TemporalFilterINSERMContrib_Description|
|
||||
__________________________________________________________________
|
||||
|
||||
Inputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_TemporalFilterINSERMContrib_Inputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_TemporalFilterINSERMContrib_Inputs|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_TemporalFilterINSERMContrib_Input1|
|
||||
* The input signal.
|
||||
* |OVP_DocEnd_BoxAlgorithm_TemporalFilterINSERMContrib_Input1|
|
||||
__________________________________________________________________
|
||||
|
||||
Outputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_TemporalFilterINSERMContrib_Outputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_TemporalFilterINSERMContrib_Outputs|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_TemporalFilterINSERMContrib_Output1|
|
||||
* |OVP_DocEnd_BoxAlgorithm_TemporalFilterINSERMContrib_Output1|
|
||||
__________________________________________________________________
|
||||
|
||||
Settings description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_TemporalFilterINSERMContrib_Settings|
|
||||
* |OVP_DocEnd_BoxAlgorithm_TemporalFilterINSERMContrib_Settings|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_TemporalFilterINSERMContrib_Setting1|
|
||||
* Select the name of filter between Butterworth and Chebyshev.
|
||||
* |OVP_DocEnd_BoxAlgorithm_TemporalFilterINSERMContrib_Setting1|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_TemporalFilterINSERMContrib_Setting2|
|
||||
* Select the kind of filter between Low pass, High pass, Band pass, Band stop.
|
||||
* |OVP_DocEnd_BoxAlgorithm_TemporalFilterINSERMContrib_Setting2|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_TemporalFilterINSERMContrib_Setting3|
|
||||
* Order of the low-pass filter.
|
||||
* |OVP_DocEnd_BoxAlgorithm_TemporalFilterINSERMContrib_Setting3|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_TemporalFilterINSERMContrib_Setting4|
|
||||
* Low edge for High pass, Band pass and Band stop filters
|
||||
* |OVP_DocEnd_BoxAlgorithm_TemporalFilterINSERMContrib_Setting4|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_TemporalFilterINSERMContrib_Setting5|
|
||||
* High edge for Low pass, Band pass and Band stop filters
|
||||
* |OVP_DocEnd_BoxAlgorithm_TemporalFilterINSERMContrib_Setting5|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_TemporalFilterINSERMContrib_Setting6|
|
||||
* If Chebyshev filter is selected, PassBand Ripple is a necessary info.
|
||||
* |OVP_DocEnd_BoxAlgorithm_TemporalFilterINSERMContrib_Setting6|
|
||||
__________________________________________________________________
|
||||
|
||||
Examples description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_TemporalFilterINSERMContrib_Examples|
|
||||
* Let's consider our input signal is very noisy (50 Hz).
|
||||
* To filter this signal, select a Low pass Butterworth filter of 4th
|
||||
* order and High PassBand Edge equal to 30 Hz for example.
|
||||
* |OVP_DocEnd_BoxAlgorithm_TemporalFilterINSERMContrib_Examples|
|
||||
__________________________________________________________________
|
||||
|
||||
Miscellaneous description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_TemporalFilterINSERMContrib_Miscellaneous|
|
||||
* This plugin filters the input signal. Several filtering are available.
|
||||
*
|
||||
* The box is able to use filter orders that are larger than the input chunk
|
||||
* size. However, with high order filters in general, remember to check that the
|
||||
* filtered output of the box remains stable and meaningful. If not,
|
||||
* try decreasing the filter order.
|
||||
*
|
||||
* |OVP_DocEnd_BoxAlgorithm_TemporalFilterINSERMContrib_Miscellaneous|
|
||||
*/
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* \page BoxAlgorithm_UnivariateStatistics Univariate Statistics
|
||||
__________________________________________________________________
|
||||
|
||||
Detailed description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Description|
|
||||
* This plugin computes the mean, variance, range, median, Inter-
|
||||
* Quantile-Range and Percentile of each incoming sample
|
||||
* buffer (or chunk) and outputs the resulting signals.
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Description|
|
||||
__________________________________________________________________
|
||||
|
||||
Inputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Inputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Inputs|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Input1|
|
||||
* The input signal.
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Input1|
|
||||
__________________________________________________________________
|
||||
|
||||
Outputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Outputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Outputs|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Output1|
|
||||
* Signal containing the averages of the input sample buffers.
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Output1|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Output2|
|
||||
* Signal containing the variance of the input sample buffers.
|
||||
* Sample variance is a measure of the spread of or dispersion
|
||||
* within a set of sample data.
|
||||
* The sample variance is the sum of the squared deviations
|
||||
* from their average divided by the number of observations in
|
||||
* the data set.
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Output2|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Output3|
|
||||
* Signal containing the range of the input sample buffers.
|
||||
* The range of a sample (or a data set) is a measure of the
|
||||
* spread or the dispersion of the observations. It is the
|
||||
* difference between the largest and the smallest observed
|
||||
* value of some quantitative characteristic.
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Output3|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Output4|
|
||||
* Signal containing the median of the input sample buffers.
|
||||
* The median is the value halfway through the ordered data
|
||||
* set, below and above which there lies an equal number of
|
||||
* data values.
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Output4|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Output5|
|
||||
* Signal containing the Inter-Quantile-Range of the input
|
||||
* sample buffers.
|
||||
* The inter-quartile range is a measure of the spread of or
|
||||
* dispersion within a data set.
|
||||
* It is calculated by taking the difference between the upper
|
||||
* and the lower quartiles.
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Output5|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Output6|
|
||||
* Signal containing the percentile of the input sample buffers.
|
||||
* Percentiles are values that divide a sample of data into one
|
||||
* hundred groups containing (as far as possible) equal numbers
|
||||
* of observations. For example, 30% of the data values lie below
|
||||
* the 30th percentile.
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Output6|
|
||||
|
||||
__________________________________________________________________
|
||||
|
||||
Settings description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Settings|
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Settings|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Setting1|
|
||||
* Mean activation. If the box is checked, the mean is computed
|
||||
* and the mean output send signal
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Setting1|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Setting2|
|
||||
* Variance activation. If the box is checked, the variance is computed
|
||||
* and the variance output send signal
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Setting2|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Setting3|
|
||||
* Range activation. If the box is checked, the range is computed
|
||||
* and the range output send signal
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Setting3|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Setting4|
|
||||
* Median activation. If the box is checked, the median is computed
|
||||
* and the median output send signal
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Setting4|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Setting5|
|
||||
* Inter-Quantile-Range (IQR) activation. If the box is checked,
|
||||
* the IQR is computed and the IQR output send signal
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Setting5|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Setting6|
|
||||
* Percentile activation. If the box is checked, the percentile
|
||||
* is computed and the percentile output send signal
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Setting6|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Setting7|
|
||||
* Percentile value. Change the percentile value for percentile
|
||||
* signal output
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Setting7|
|
||||
|
||||
__________________________________________________________________
|
||||
|
||||
Examples description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Examples|
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Examples|
|
||||
__________________________________________________________________
|
||||
|
||||
Miscellaneous description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_UnivariateStatistics_Miscellaneous|
|
||||
* The output signal's sample count per channel per buffer is one,
|
||||
* since a buffer contains the averages (per channel) of the values
|
||||
* of an input buffer.
|
||||
* Be careful of Down-sampling effect for signal, the sampling rate
|
||||
* at output is divided by the number of input samples. This information
|
||||
* is saved on integer, so this stay true only if division is without
|
||||
* fractional part (or the subsampling should be a divisor of the number
|
||||
* of samples)
|
||||
* http://www.stats.gla.ac.uk/steps/glossary/presenting_data.html
|
||||
* |OVP_DocEnd_BoxAlgorithm_UnivariateStatistics_Miscellaneous|
|
||||
*/
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* \page BoxAlgorithm_WindowingINSERMContrib Windowing functions -
|
||||
__________________________________________________________________
|
||||
|
||||
Detailed description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_WindowingINSERMContrib_Description|
|
||||
* This plugin is used to apply a window to the input signal.
|
||||
* This plugin allows the selection of the kind of window
|
||||
* (Hamming, Hanning, Hann, Blackman, Triangular, Square Root).
|
||||
* |OVP_DocEnd_BoxAlgorithm_WindowingINSERMContrib_Description|
|
||||
__________________________________________________________________
|
||||
|
||||
Inputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_WindowingINSERMContrib_Inputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_WindowingINSERMContrib_Inputs|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_WindowingINSERMContrib_Input1|
|
||||
* |OVP_DocEnd_BoxAlgorithm_WindowingINSERMContrib_Input1|
|
||||
__________________________________________________________________
|
||||
|
||||
Outputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_WindowingINSERMContrib_Outputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_WindowingINSERMContrib_Outputs|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_WindowingINSERMContrib_Output1|
|
||||
* |OVP_DocEnd_BoxAlgorithm_WindowingINSERMContrib_Output1|
|
||||
__________________________________________________________________
|
||||
|
||||
Settings description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_WindowingINSERMContrib_Settings|
|
||||
* |OVP_DocEnd_BoxAlgorithm_WindowingINSERMContrib_Settings|
|
||||
*
|
||||
* |OVP_DocBegin_BoxAlgorithm_WindowingINSERMContrib_Setting1|
|
||||
* Select the name of window between Hamming, Hanning, Hann,
|
||||
* Blackman, Triangular and Square Root.
|
||||
* |OVP_DocEnd_BoxAlgorithm_WindowingINSERMContrib_Setting1|
|
||||
__________________________________________________________________
|
||||
|
||||
Examples description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_WindowingINSERMContrib_Examples|
|
||||
* Let's consider our input signal.
|
||||
* To prevent rebound in spectrum analysis due to the square root
|
||||
* windowing, select a Hanning window for example.
|
||||
* |OVP_DocEnd_BoxAlgorithm_WindowingINSERMContrib_Examples|
|
||||
__________________________________________________________________
|
||||
|
||||
Miscellaneous description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_WindowingINSERMContrib_Miscellaneous|
|
||||
* This plugin applies a window (weighting function) to each of the channels of the
|
||||
* input signal. Several windows are available. Note that the window is applied
|
||||
* separately per each chunk and each channel. It does not 'slide' over the streaming data.
|
||||
* |OVP_DocEnd_BoxAlgorithm_WindowingINSERMContrib_Miscellaneous|
|
||||
*/
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
+675
@@ -0,0 +1,675 @@
|
||||
<OpenViBE-Scenario>
|
||||
<FormatVersion>2</FormatVersion>
|
||||
<Creator>OpenViBE Designer</Creator>
|
||||
<CreatorVersion>2.2.0</CreatorVersion>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<Identifier>(0x53e968eb, 0x121c769a)</Identifier>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Low Cut Frequency</Name>
|
||||
<DefaultValue>5</DefaultValue>
|
||||
<Value>5</Value>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<Identifier>(0x8cad39ce, 0xdbca80d0)</Identifier>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>High Cut Frequency</Name>
|
||||
<DefaultValue>35</DefaultValue>
|
||||
<Value>35</Value>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<Identifier>(0x454af4a9, 0x3e55e77a)</Identifier>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Epoch Duration</Name>
|
||||
<DefaultValue>4</DefaultValue>
|
||||
<Value>4</Value>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<Identifier>(0xbb21ad6d, 0xda6c49a5)</Identifier>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Epoch Offset</Name>
|
||||
<DefaultValue>0.5</DefaultValue>
|
||||
<Value>0.5</Value>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<Identifier>(0xc956238b, 0xf31b7a91)</Identifier>
|
||||
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
|
||||
<Name>Class 1</Name>
|
||||
<DefaultValue>OVTK_GDF_Left</DefaultValue>
|
||||
<Value>OVTK_GDF_Left</Value>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<Identifier>(0xe28ee4db, 0xb8abaac1)</Identifier>
|
||||
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
|
||||
<Name>Class 2</Name>
|
||||
<DefaultValue>OVTK_GDF_Right</DefaultValue>
|
||||
<Value>OVTK_GDF_Right</Value>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<Identifier>(0x5b25c57b, 0xbcf2329e)</Identifier>
|
||||
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
|
||||
<Name>Train Trigger</Name>
|
||||
<DefaultValue>OVTK_StimulationId_Train</DefaultValue>
|
||||
<Value>OVTK_StimulationId_Train</Value>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<Identifier>(0x4c8b6ef1, 0xf834b8a0)</Identifier>
|
||||
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
|
||||
<Name>Spatial Filter Configuration</Name>
|
||||
<DefaultValue>${Player_ScenarioDirectory}/scripts/csp-spatial-filter.xml</DefaultValue>
|
||||
<Value>${Player_ScenarioDirectory}/scripts/csp-spatial-filter.xml</Value>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<Identifier>(0x2ad32f66, 0xd86a0623)</Identifier>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Filter Dimension</Name>
|
||||
<DefaultValue>6</DefaultValue>
|
||||
<Value>6</Value>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<Identifier>(0xe02590b0, 0x52f82985)</Identifier>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Input Signal</Name>
|
||||
<LinkedBoxIdentifier>(0x000016d5, 0x000033f5)</LinkedBoxIdentifier>
|
||||
<LinkedBoxInputIndex>0</LinkedBoxInputIndex>
|
||||
</Input>
|
||||
<Input>
|
||||
<Identifier>(0x9493bcb8, 0xd8d4848f)</Identifier>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Input Stimulations</Name>
|
||||
<LinkedBoxIdentifier>(0x00001c84, 0x0000598b)</LinkedBoxIdentifier>
|
||||
<LinkedBoxInputIndex>0</LinkedBoxInputIndex>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<Identifier>(0xd3d905f7, 0x2b88c1ac)</Identifier>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Train-completed Flag</Name>
|
||||
<LinkedBoxIdentifier>(0x00005515, 0x00006ba1)</LinkedBoxIdentifier>
|
||||
<LinkedBoxOutputIndex>0</LinkedBoxOutputIndex>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Boxes>
|
||||
<Box>
|
||||
<Identifier>(0x000016d5, 0x000033f5)</Identifier>
|
||||
<Name>Temporal Filter</Name>
|
||||
<AlgorithmClassIdentifier>(0xb4f9d042, 0x9d79f2e5)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Input signal</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Filtered signal</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2f2c606c, 0x8512ed68)</TypeIdentifier>
|
||||
<Name>Filter method</Name>
|
||||
<DefaultValue>Butterworth</DefaultValue>
|
||||
<Value>Butterworth</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0xfa20178e, 0x4cba62e9)</TypeIdentifier>
|
||||
<Name>Filter type</Name>
|
||||
<DefaultValue>Band pass</DefaultValue>
|
||||
<Value>Band pass</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Filter order</Name>
|
||||
<DefaultValue>4</DefaultValue>
|
||||
<Value>5</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Low cut frequency (Hz)</Name>
|
||||
<DefaultValue>29</DefaultValue>
|
||||
<Value>$var{Low Cut Frequency}</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>High cut frequency (Hz)</Name>
|
||||
<DefaultValue>40</DefaultValue>
|
||||
<Value>$var{High Cut Frequency}</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Pass band ripple (dB)</Name>
|
||||
<DefaultValue>0.5</DefaultValue>
|
||||
<Value>1</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>304</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>1104</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x27a4ceec, 0x876d6384)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
|
||||
<Value>(0x00000000, 0x002e3671)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>6</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00001aa0, 0x00005f82)</Identifier>
|
||||
<Name>Class 1</Name>
|
||||
<AlgorithmClassIdentifier>(0x426163d1, 0x324237b0)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Input signal</Name>
|
||||
</Input>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Input stimulations</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Epoched signal</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Epoch duration (in sec)</Name>
|
||||
<DefaultValue>1</DefaultValue>
|
||||
<Value>$var{Epoch Duration}</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Epoch offset (in sec)</Name>
|
||||
<DefaultValue>0.5</DefaultValue>
|
||||
<Value>$var{Epoch Offset}</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
|
||||
<Name>Stimulation to epoch from</Name>
|
||||
<DefaultValue>OVTK_StimulationId_Label_00</DefaultValue>
|
||||
<Value>$var{Class 1}</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>384</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>1024</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0xa79941ae, 0x80708445)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>3</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00001aa0, 0x00005f83)</Identifier>
|
||||
<Name>Class 2</Name>
|
||||
<AlgorithmClassIdentifier>(0x426163d1, 0x324237b0)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Input signal</Name>
|
||||
</Input>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Input stimulations</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Epoched signal</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Epoch duration (in sec)</Name>
|
||||
<DefaultValue>1</DefaultValue>
|
||||
<Value>$var{Epoch Duration}</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Epoch offset (in sec)</Name>
|
||||
<DefaultValue>0.5</DefaultValue>
|
||||
<Value>$var{Epoch Offset}</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
|
||||
<Name>Stimulation to epoch from</Name>
|
||||
<DefaultValue>OVTK_StimulationId_Label_00</DefaultValue>
|
||||
<Value>$var{Class 2}</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>384</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>1184</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0xa79941ae, 0x80708445)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>3</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00001c84, 0x0000598b)</Identifier>
|
||||
<Name>Identity</Name>
|
||||
<AlgorithmClassIdentifier>(0x5dffe431, 0x35215c50)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Input stream</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Output stream</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x17ee7c08, 0x94c14893)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>304</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>912</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x30a4e5c9, 0x83502953)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0xa8ffe2a3, 0x27038f03)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00001c84, 0x0000598c)</Identifier>
|
||||
<Name>Identity</Name>
|
||||
<AlgorithmClassIdentifier>(0x5dffe431, 0x35215c50)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Input stream</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Output stream</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x17ee7c08, 0x94c14893)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>416</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>912</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x30a4e5c9, 0x83502953)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0xa8ffe2a3, 0x27038f03)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00005515, 0x00006ba1)</Identifier>
|
||||
<Name>CSP Spatial Filter Trainer</Name>
|
||||
<AlgorithmClassIdentifier>(0x51db0d64, 0x2109714e)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Stimulations</Name>
|
||||
</Input>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Signal condition 1</Name>
|
||||
</Input>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Signal condition 2</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Train-completed Flag</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
|
||||
<Name>Train Trigger</Name>
|
||||
<DefaultValue>OVTK_GDF_End_Of_Session</DefaultValue>
|
||||
<Value>OVTK_StimulationId_Train</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
|
||||
<Name>Spatial filter configuration</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value>$var{Spatial Filter Configuration}</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Filter dimension</Name>
|
||||
<DefaultValue>2</DefaultValue>
|
||||
<Value>$var{Filter Dimension}</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Save as box config</Name>
|
||||
<DefaultValue>true</DefaultValue>
|
||||
<Value>true</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>464</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>1104</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x4b49a133, 0x42f38d94)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
|
||||
<Value>(0x00000000, 0x002a4d2d)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
|
||||
<Value>false</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>4</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>3</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
</Boxes>
|
||||
<Links>
|
||||
<Link>
|
||||
<Identifier>(0x00000c54, 0x000048d9)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00001aa0, 0x00005f82)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00005515, 0x00006ba1)</BoxIdentifier>
|
||||
<BoxInputIndex>1</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00000cdf, 0x000029b9)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00001c84, 0x0000598c)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00005515, 0x00006ba1)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x0000104a, 0x00004869)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00001c84, 0x0000598b)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00001aa0, 0x00005f82)</BoxIdentifier>
|
||||
<BoxInputIndex>1</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00001135, 0x00003548)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x000016d5, 0x000033f5)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00001aa0, 0x00005f83)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x000015b8, 0x00000ac7)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00001c84, 0x0000598b)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00001aa0, 0x00005f83)</BoxIdentifier>
|
||||
<BoxInputIndex>1</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00002287, 0x000005db)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x000016d5, 0x000033f5)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00001aa0, 0x00005f82)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00002d57, 0x00004b2f)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00001c84, 0x0000598b)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00001c84, 0x0000598c)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00007d7b, 0x000049f7)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00001aa0, 0x00005f83)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00005515, 0x00006ba1)</BoxIdentifier>
|
||||
<BoxInputIndex>2</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
</Links>
|
||||
<Comments></Comments>
|
||||
<Metadata>
|
||||
<Entry>
|
||||
<Identifier>(0x00004fc8, 0x00005801)</Identifier>
|
||||
<Type>(0x3bcce5d2, 0x43f2d968)</Type>
|
||||
<Data>[{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"height":320,"identifier":"(0x00006ba8, 0x000061b1)","name":"Default window","parentIdentifier":"(0xffffffff, 0xffffffff)","type":1,"width":480},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"identifier":"(0x000023d0, 0x00003bbe)","index":0,"name":"Default tab","parentIdentifier":"(0x00006ba8, 0x000061b1)","type":2},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":0,"identifier":"(0x0000383b, 0x00001860)","index":0,"name":"Empty","parentIdentifier":"(0x000023d0, 0x00003bbe)","type":0}]</Data>
|
||||
</Entry>
|
||||
</Metadata>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x079d1f09, 0x667c7871)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x198dd621, 0xab7f153f)</Identifier>
|
||||
<Value>(0x18bc5b7e, 0xe8b6d7da)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x20c6d158, 0xfe90aadd)</Identifier>
|
||||
<Value>(0x05a13f95, 0xdfdc0e8f)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x60bc58ab, 0x7a1b4d83)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x790d75b8, 0x3bb90c33)</Identifier>
|
||||
<Value>Thibaut Monseigne</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x7a3a1558, 0xf12c63c2)</Identifier>
|
||||
<Value>Add Temporal Filter (Butterworth Order 5) and separate in two class before Train</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x84009d7c, 0x3c4e763b)</Identifier>
|
||||
<Value>Make typical process before train</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x8c1fc55b, 0x7b433dc2)</Identifier>
|
||||
<Value>1.0</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x9f5c4075, 0x4a0d3666)</Identifier>
|
||||
<Value>CSP Spatial Filter Trainer Metabox</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf36a1567, 0xd13c53da)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf6b2e3fa, 0x7bd43926)</Identifier>
|
||||
<Value>Signal processing/Filtering/</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf8034a49, 0x8b3f37cc)</Identifier>
|
||||
<Value>Inria</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</OpenViBE-Scenario>
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
#include "ovpCAlgorithmUnivariateStatistics.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
// ________________________________________________________________________________________________________________
|
||||
//
|
||||
|
||||
bool CAlgoUnivariateStatistic::initialize()
|
||||
{
|
||||
ip_matrix.initialize(getInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_Matrix));
|
||||
op_MeanMatrix.initialize(getOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_Mean));
|
||||
op_VarianceMatrix.initialize(getOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_Var));
|
||||
op_RangeMatrix.initialize(getOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_Range));
|
||||
op_MedianMatrix.initialize(getOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_Med));
|
||||
op_IQRMatrix.initialize(getOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_IQR));
|
||||
op_PercentileMatrix.initialize(getOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_Percent));
|
||||
|
||||
ip_isMeanActive.initialize(this->getInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_MeanActive));
|
||||
ip_isVarianceActive.initialize(this->getInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_VarActive));
|
||||
ip_isRangeActive.initialize(this->getInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_RangeActive));
|
||||
ip_isMedianActive.initialize(this->getInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_MedActive));
|
||||
ip_isIQRActive.initialize(this->getInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_IQRActive));
|
||||
ip_isPercentileActive.initialize(this->getInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_PercentActive));
|
||||
|
||||
ip_percentileValue.initialize(this->getInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_PercentValue));
|
||||
op_compression.initialize(this->getOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_Compression));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAlgoUnivariateStatistic::uninitialize()
|
||||
{
|
||||
op_compression.uninitialize();
|
||||
ip_percentileValue.uninitialize();
|
||||
|
||||
ip_isMeanActive.uninitialize();
|
||||
ip_isVarianceActive.uninitialize();
|
||||
ip_isRangeActive.uninitialize();
|
||||
ip_isMedianActive.uninitialize();
|
||||
ip_isIQRActive.uninitialize();
|
||||
ip_isPercentileActive.uninitialize();
|
||||
|
||||
op_MeanMatrix.uninitialize();
|
||||
op_VarianceMatrix.uninitialize();
|
||||
op_RangeMatrix.uninitialize();
|
||||
op_MedianMatrix.uninitialize();
|
||||
op_IQRMatrix.uninitialize();
|
||||
op_PercentileMatrix.uninitialize();
|
||||
ip_matrix.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ________________________________________________________________________________________________________________
|
||||
//
|
||||
|
||||
bool CAlgoUnivariateStatistic::process()
|
||||
{
|
||||
CMatrix* matrix = ip_matrix;
|
||||
CMatrix* mean = op_MeanMatrix;
|
||||
CMatrix* variance = op_VarianceMatrix;
|
||||
CMatrix* range = op_RangeMatrix;
|
||||
CMatrix* median = op_MedianMatrix;
|
||||
CMatrix* iqr = op_IQRMatrix;
|
||||
CMatrix* percentile = op_PercentileMatrix;
|
||||
|
||||
if (this->isInputTriggerActive(OVP_Algorithm_UnivariateStatistic_InputTriggerId_Initialize))
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << "input : " << matrix->getDimensionCount() << " : " << matrix->getDimensionSize(0) << "*" <<
|
||||
matrix->getDimensionSize(1) << "\n";
|
||||
|
||||
//initialize matrix output
|
||||
if (!setMatrixDimension(mean, matrix)) { return false; }
|
||||
if (!setMatrixDimension(variance, matrix)) { return false; }
|
||||
if (!setMatrixDimension(range, matrix)) { return false; }
|
||||
if (!setMatrixDimension(median, matrix)) { return false; }
|
||||
if (!setMatrixDimension(iqr, matrix)) { return false; }
|
||||
if (!setMatrixDimension(percentile, matrix)) { return false; }
|
||||
|
||||
//inform about the compression on sampling rate due to this operation N->1 :=: fq->fq/N
|
||||
op_compression = 1 / double(matrix->getDimensionSize(1));
|
||||
|
||||
//percentile value
|
||||
m_percentileValue = ip_percentileValue;
|
||||
//select operation to do (avoid unuseful calculus)
|
||||
m_isSumActive = ip_isMeanActive || ip_isVarianceActive;
|
||||
m_isSqaresumActive = ip_isVarianceActive;
|
||||
m_isSortActive = ip_isRangeActive || ip_isMedianActive || ip_isIQRActive || ip_isPercentileActive;
|
||||
|
||||
if (m_isSumActive)
|
||||
{
|
||||
m_sumMatrix.copyDescription(*matrix);
|
||||
m_sumMatrix.setDimensionSize(1, 1);
|
||||
}
|
||||
|
||||
if (m_isSqaresumActive)
|
||||
{
|
||||
m_sumMatrix2.copyDescription(*matrix);
|
||||
m_sumMatrix2.setDimensionSize(1, 1);
|
||||
}
|
||||
|
||||
if (m_isSortActive) { m_sortMatrix.copyDescription(*matrix); }
|
||||
}
|
||||
|
||||
if (this->isInputTriggerActive(OVP_Algorithm_UnivariateStatistic_InputTriggerId_Process))
|
||||
{
|
||||
///make necessary operations
|
||||
//dimension
|
||||
const double s = double(matrix->getDimensionSize(1));
|
||||
//sum, sum square, sort
|
||||
std::vector<double> vect(matrix->getDimensionSize(1));
|
||||
for (size_t i = 0; i < matrix->getDimensionSize(0); ++i)
|
||||
{
|
||||
if (m_isSortActive)
|
||||
{
|
||||
//copy fonctionne pas car le buffer n'est pas unidirectionnel...
|
||||
for (size_t j = 0; j < matrix->getDimensionSize(1); ++j) { vect[j] = matrix->getBuffer()[i * matrix->getDimensionSize(1) + j]; }
|
||||
std::sort(vect.begin(), vect.end());
|
||||
}
|
||||
|
||||
double y = 0, y2 = 0;
|
||||
for (size_t j = 0; j < matrix->getDimensionSize(1); ++j)
|
||||
{
|
||||
const double x = matrix->getBuffer()[i * matrix->getDimensionSize(1) + j];
|
||||
if (m_isSumActive) { y += x; }
|
||||
if (m_isSqaresumActive) { y2 += x * x; }
|
||||
if (m_isSortActive) { m_sortMatrix.getBuffer()[i * m_sortMatrix.getDimensionSize(1) + j] = vect.at(j); }
|
||||
}
|
||||
|
||||
if (m_isSumActive) { m_sumMatrix.getBuffer()[i * m_sumMatrix.getDimensionSize(1)] = y; }
|
||||
if (m_isSqaresumActive) { m_sumMatrix2.getBuffer()[i * m_sumMatrix2.getDimensionSize(1)] = y2; }
|
||||
}
|
||||
|
||||
///make statistics operations...
|
||||
if (ip_isMeanActive)
|
||||
{
|
||||
for (size_t i = 0; i < mean->getDimensionSize(0); ++i)
|
||||
{
|
||||
mean->getBuffer()[i * mean->getDimensionSize(1)] = m_sumMatrix.getBuffer()[i * m_sumMatrix.getDimensionSize(1)] / matrix->getDimensionSize(1);
|
||||
}
|
||||
}
|
||||
if (ip_isVarianceActive)
|
||||
{
|
||||
for (size_t i = 0; i < variance->getDimensionSize(0); ++i)
|
||||
{
|
||||
const double y = m_sumMatrix.getBuffer()[i * m_sumMatrix.getDimensionSize(1)];
|
||||
const double y2 = m_sumMatrix2.getBuffer()[i * m_sumMatrix2.getDimensionSize(1)];
|
||||
|
||||
variance->getBuffer()[i * variance->getDimensionSize(1)] = y2 / s - y * y / (s * s);
|
||||
}
|
||||
}
|
||||
if (ip_isRangeActive)
|
||||
{
|
||||
for (size_t i = 0; i < range->getDimensionSize(0); ++i)
|
||||
{
|
||||
const double min = m_sortMatrix.getBuffer()[i * m_sortMatrix.getDimensionSize(1) + 0];
|
||||
const double max = m_sortMatrix.getBuffer()[(i + 1) * m_sortMatrix.getDimensionSize(1) - 1];
|
||||
|
||||
range->getBuffer()[i * range->getDimensionSize(1)] = max - min;
|
||||
}
|
||||
}
|
||||
if (ip_isMedianActive)
|
||||
{
|
||||
for (size_t i = 0; i < median->getDimensionSize(0); ++i)
|
||||
{
|
||||
median->getBuffer()[i * median->getDimensionSize(1)] =
|
||||
(m_sortMatrix.getDimensionSize(1) % 2)
|
||||
? m_sortMatrix.getBuffer()[i * m_sortMatrix.getDimensionSize(1) + m_sortMatrix.getDimensionSize(1) / 2 + 1 - 1]
|
||||
: (m_sortMatrix.getBuffer()[i * m_sortMatrix.getDimensionSize(1) + m_sortMatrix.getDimensionSize(1) / 2 - 1]
|
||||
+ m_sortMatrix.getBuffer()[i * m_sortMatrix.getDimensionSize(1) + m_sortMatrix.getDimensionSize(1) / 2 + 1 - 1]) / 2;
|
||||
}
|
||||
}
|
||||
if (ip_isIQRActive)
|
||||
{
|
||||
for (size_t i = 0; i < iqr->getDimensionSize(0); ++i)
|
||||
{
|
||||
double flow = 0, up = 0;
|
||||
const size_t reste = m_sortMatrix.getDimensionSize(1) % 4;
|
||||
const size_t nb = 4 - reste;
|
||||
for (size_t k = 0; k < nb; ++k)
|
||||
{
|
||||
flow += m_sortMatrix.getBuffer()[i * m_sortMatrix.getDimensionSize(1) + m_sortMatrix.getDimensionSize(1) / 4 - (nb - 1) + k - 1];
|
||||
}
|
||||
|
||||
flow /= nb;
|
||||
|
||||
for (size_t k = 0; k < nb; ++k)
|
||||
{
|
||||
up += m_sortMatrix.getBuffer()[i * m_sortMatrix.getDimensionSize(1) + m_sortMatrix.getDimensionSize(1)
|
||||
- m_sortMatrix.getDimensionSize(1) / 4 - 1 + k - 1];
|
||||
}
|
||||
|
||||
up /= nb;
|
||||
iqr->getBuffer()[i * iqr->getDimensionSize(1)] = up - flow;
|
||||
}
|
||||
}
|
||||
if (ip_isPercentileActive)
|
||||
{
|
||||
const uint64_t value = m_percentileValue;
|
||||
for (size_t i = 0; i < percentile->getDimensionSize(0); ++i)
|
||||
{
|
||||
percentile->getBuffer()[i * percentile->getDimensionSize(1)] = m_sortMatrix.getBuffer()[
|
||||
i * m_sortMatrix.getDimensionSize(1) + std::max(int(0), int(m_sortMatrix.getDimensionSize(1) * value / 100 - 1))];
|
||||
}
|
||||
}
|
||||
|
||||
this->activateOutputTrigger(OVP_Algorithm_UnivariateStatistic_OutputTriggerId_ProcessDone, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAlgoUnivariateStatistic::setMatrixDimension(CMatrix* matrix, CMatrix* ref)
|
||||
{
|
||||
//@todo We Allow matrix with 3 or more dimension ?
|
||||
if (ref->getDimensionCount() < 2)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Warning << "Input matrix doesn't respect basic criteria (2 Dimensions)\n";
|
||||
return false;
|
||||
}
|
||||
matrix->copyDescription(*ref);
|
||||
return true;
|
||||
}
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
class CAlgoUnivariateStatistic final : public Toolkit::TAlgorithm<IAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TAlgorithm<IAlgorithm>, OVP_ClassId_AlgoUnivariateStatistic)
|
||||
|
||||
protected:
|
||||
|
||||
Kernel::TParameterHandler<CMatrix*> ip_matrix;
|
||||
Kernel::TParameterHandler<CMatrix*> op_MeanMatrix;
|
||||
Kernel::TParameterHandler<CMatrix*> op_VarianceMatrix;
|
||||
Kernel::TParameterHandler<CMatrix*> op_RangeMatrix;
|
||||
Kernel::TParameterHandler<CMatrix*> op_MedianMatrix;
|
||||
Kernel::TParameterHandler<CMatrix*> op_IQRMatrix;
|
||||
Kernel::TParameterHandler<CMatrix*> op_PercentileMatrix;
|
||||
|
||||
Kernel::TParameterHandler<bool> ip_isMeanActive;
|
||||
Kernel::TParameterHandler<bool> ip_isVarianceActive;
|
||||
Kernel::TParameterHandler<bool> ip_isRangeActive;
|
||||
Kernel::TParameterHandler<bool> ip_isMedianActive;
|
||||
Kernel::TParameterHandler<bool> ip_isIQRActive;
|
||||
Kernel::TParameterHandler<bool> ip_isPercentileActive;
|
||||
Kernel::TParameterHandler<uint64_t> ip_percentileValue;
|
||||
|
||||
Kernel::TParameterHandler<double> op_compression;
|
||||
|
||||
bool m_isSumActive = false;
|
||||
bool m_isSqaresumActive = false;
|
||||
bool m_isSortActive = false;
|
||||
CMatrix m_sumMatrix;
|
||||
CMatrix m_sumMatrix2;
|
||||
CMatrix m_sortMatrix;
|
||||
|
||||
uint64_t m_percentileValue = 0;
|
||||
|
||||
bool setMatrixDimension(CMatrix* matrix, CMatrix* ref);
|
||||
};
|
||||
|
||||
class CAlgoUnivariateStatisticDesc final : public IAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Signal Statistic"); }
|
||||
CString getAuthorName() const override { return CString("Matthieu Goyat"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Gipsa-lab"); }
|
||||
|
||||
CString getShortDescription() const override { return CString("Calculate Mean, Variance, Median, etc. on the incoming buffer"); }
|
||||
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString("Signal processing/Statistics"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_AlgoUnivariateStatistic; }
|
||||
IPluginObject* create() override { return new CAlgoUnivariateStatistic(); }
|
||||
|
||||
bool getAlgorithmPrototype(Kernel::IAlgorithmProto& prototype) const override
|
||||
{
|
||||
prototype.addInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_Matrix, "Matrix input", Kernel::ParameterType_Matrix);
|
||||
prototype.addInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_MeanActive, "active mean", Kernel::ParameterType_Boolean);
|
||||
prototype.addInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_VarActive, "active variance", Kernel::ParameterType_Boolean);
|
||||
prototype.addInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_RangeActive, "active range", Kernel::ParameterType_Boolean);
|
||||
prototype.addInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_MedActive, "active median", Kernel::ParameterType_Boolean);
|
||||
prototype.addInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_IQRActive, "active IQR", Kernel::ParameterType_Boolean);
|
||||
prototype.addInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_PercentActive, "active Percentile", Kernel::ParameterType_Boolean);
|
||||
prototype.addInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_PercentValue, "Percentile Value", Kernel::ParameterType_Integer);
|
||||
prototype.addOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_Mean, "Mean output", Kernel::ParameterType_Matrix);
|
||||
prototype.addOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_Var, "Variance output", Kernel::ParameterType_Matrix);
|
||||
prototype.addOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_Range, "Range output", Kernel::ParameterType_Matrix);
|
||||
prototype.addOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_Med, "Median output", Kernel::ParameterType_Matrix);
|
||||
prototype.addOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_IQR, "Inter-Quantile-Range output", Kernel::ParameterType_Matrix);
|
||||
prototype.addOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_Percent, "Percentile output", Kernel::ParameterType_Matrix);
|
||||
prototype.addOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_Compression, "compression ratio", Kernel::ParameterType_Float);
|
||||
|
||||
prototype.addInputTrigger(OVP_Algorithm_UnivariateStatistic_InputTriggerId_Initialize, "Initialize");
|
||||
prototype.addInputTrigger(OVP_Algorithm_UnivariateStatistic_InputTriggerId_Process, "Process");
|
||||
prototype.addOutputTrigger(OVP_Algorithm_UnivariateStatistic_OutputTriggerId_ProcessDone, "Done");
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IAlgorithmDesc, OVP_ClassId_AlgoUnivariateStatisticDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+324
@@ -0,0 +1,324 @@
|
||||
#if defined TARGET_HAS_ThirdPartyITPP
|
||||
|
||||
#include "ovpCApplyTemporalFilter.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
void ComputeFilterInitialCondition(itpp::vec b, itpp::vec a, itpp::vec& zi)
|
||||
{
|
||||
int na, j, i;
|
||||
na = length(a);
|
||||
// FIXME is it necessary to keep next line uncomment ?
|
||||
//int nb = length(b);
|
||||
|
||||
//--------------------------------------
|
||||
// use sparse matrix to solve system of linear equations for initial conditions
|
||||
// zi are the steady-state states of the filter b(z)/a(z) in the state-space
|
||||
//implementation of the 'filter' command.
|
||||
|
||||
itpp::mat eye1;
|
||||
eye1 = itpp::eye(na - 1);
|
||||
|
||||
itpp::mat eye2;
|
||||
eye2 = itpp::eye(na - 2);
|
||||
|
||||
itpp::vec a1(na - 1);
|
||||
itpp::vec zeros1(na - 2);
|
||||
zeros1 = itpp::zeros(na - 2);
|
||||
itpp::vec b1(na - 1);
|
||||
itpp::vec a2(na - 1);
|
||||
|
||||
for (j = 1; j < na; ++j)
|
||||
{
|
||||
a1[j - 1] = - a[j];
|
||||
b1[j - 1] = b[j];
|
||||
a2[j - 1] = a[j];
|
||||
}
|
||||
|
||||
itpp::mat matConc01;
|
||||
itpp::mat matZeros1;
|
||||
|
||||
matZeros1 = itpp::zeros(na - 2, 1);
|
||||
matConc01 = concat_vertical(eye2, transpose(matZeros1));
|
||||
|
||||
itpp::mat matA1;
|
||||
matA1 = itpp::zeros(na - 1, 1);
|
||||
matA1.set_col(0, a1);
|
||||
|
||||
itpp::mat matConc02;
|
||||
matConc02 = concat_horizontal(matA1, matConc01);
|
||||
|
||||
itpp::mat matNum;
|
||||
matNum = eye1 - matConc02;
|
||||
|
||||
|
||||
itpp::vec vecDenom(na - 1);
|
||||
for (i = 0; i < na - 1; ++i) { vecDenom[i] = b1[i] - (a2[i] * b[0]); }
|
||||
|
||||
zi = inv(matNum) * vecDenom;
|
||||
}
|
||||
|
||||
|
||||
void FilterIRR(itpp::vec b, itpp::vec a, itpp::vec data, itpp::vec v0, itpp::vec& dataFiltered, itpp::vec& vf)
|
||||
{
|
||||
int i, j, iV0 = 0;
|
||||
double sumA, sumB;
|
||||
double sumVf;
|
||||
// FIXME is it necessary to keep next line uncomment ?
|
||||
//int na = length(a);
|
||||
const int nb = length(b);
|
||||
const int size = length(data);
|
||||
|
||||
if (size < nb)
|
||||
{
|
||||
for (i = 0; i < size; ++i)
|
||||
{
|
||||
sumB = 0.0;
|
||||
for (j = 0; j <= i; ++j) { sumB = sumB + (b[j] * data[i - j]); }
|
||||
|
||||
|
||||
sumA = 0.0;
|
||||
|
||||
for (j = 0; j <= i; ++j) { sumA = sumA + (a[j] * dataFiltered[i - j]); }
|
||||
dataFiltered[i] = sumB - sumA + v0[i];
|
||||
}
|
||||
|
||||
for (i = 0; i < (nb - 1); ++i)
|
||||
{
|
||||
sumVf = 0.0;
|
||||
double tmp = 0.0;
|
||||
for (j = 0; j < (nb - 1); ++j)
|
||||
{
|
||||
if ((i + j) < (nb - 1))
|
||||
{
|
||||
if ((size - 1 - j) >= 0)
|
||||
{
|
||||
sumVf = sumVf + (b[i + j + 1] * data[size - 1 - j]) - (a[i + j + 1] * dataFiltered[size - 1 - j]);
|
||||
iV0 = i + j + 1;
|
||||
}
|
||||
if ((size - 1 - j) < 0) { tmp = v0[iV0]; }
|
||||
}
|
||||
}
|
||||
vf[i] = sumVf + tmp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < nb - 1; ++i)
|
||||
{
|
||||
sumB = 0.0;
|
||||
for (j = 0; j <= i; ++j) { sumB = sumB + (b[j] * data[i - j]); }
|
||||
sumA = 0.0;
|
||||
|
||||
for (j = 0; j <= i; ++j) { sumA = sumA + (a[j] * dataFiltered[i - j]); }
|
||||
dataFiltered[i] = sumB - sumA + v0[i];
|
||||
}
|
||||
|
||||
|
||||
for (i = nb - 1; i < size; ++i)
|
||||
{
|
||||
sumB = 0.0;
|
||||
for (j = 0; j < nb; ++j) { sumB = sumB + (b[j] * data[i - j]); }
|
||||
sumA = 0.0;
|
||||
for (j = 0; j < nb; ++j) { sumA = sumA + (a[j] * dataFiltered[i - j]); }
|
||||
dataFiltered[i] = sumB - sumA;
|
||||
}
|
||||
|
||||
for (i = 0; i < nb - 1; ++i)
|
||||
{
|
||||
sumVf = 0.0;
|
||||
for (j = i; j < nb - 1; ++j) { sumVf = sumVf + (b[j + 1] * data[size - 1 - j + i]) - (a[j + 1] * dataFiltered[size - 1 - j + i]); }
|
||||
vf[i] = sumVf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Filtfilt(const itpp::vec& b, const itpp::vec& a, itpp::vec data, itpp::vec& dataFiltered)
|
||||
{
|
||||
int j;
|
||||
const int na = length(a);
|
||||
const int nb = length(b);
|
||||
const int dataSize = length(data);
|
||||
const int lengthEdgeTransients = 3 * (nb - 1);
|
||||
|
||||
itpp::vec xB = itpp::zeros(dataSize + (2 * lengthEdgeTransients));
|
||||
itpp::vec yB = itpp::zeros(dataSize + (2 * lengthEdgeTransients));
|
||||
itpp::vec yB2 = itpp::zeros(dataSize + (2 * lengthEdgeTransients));
|
||||
itpp::vec yC = itpp::zeros(dataSize + (2 * lengthEdgeTransients));
|
||||
itpp::vec yC2 = itpp::zeros(dataSize + (2 * lengthEdgeTransients));
|
||||
|
||||
|
||||
itpp::vec zi(na - 1);
|
||||
ComputeFilterInitialCondition(b, a, zi);
|
||||
|
||||
for (j = 0; j < lengthEdgeTransients; ++j) { xB[j] = (2 * data[0]) - data[lengthEdgeTransients - j]; }
|
||||
for (j = 0; j < dataSize; ++j) { xB[j + lengthEdgeTransients] = data[j]; }
|
||||
for (j = 0; j < lengthEdgeTransients; ++j) { xB[j + lengthEdgeTransients + dataSize] = (2 * data[dataSize - 1]) - data[dataSize - j - 2]; }
|
||||
|
||||
itpp::vec ziChan(na - 1);
|
||||
for (j = 0; j < na - 1; ++j) { ziChan[j] = zi[j] * xB[0]; }
|
||||
|
||||
itpp::vec finalStates(na - 1);
|
||||
|
||||
FilterIRR(b, a, xB, ziChan, yB, finalStates);
|
||||
|
||||
for (j = 0; j < dataSize + (2 * lengthEdgeTransients); ++j) { yC[j] = yB[(dataSize + (2 * lengthEdgeTransients)) - 1 - j]; }
|
||||
|
||||
itpp::vec ziChan2(na - 1);
|
||||
|
||||
for (j = 0; j < na - 1; ++j) { ziChan2[j] = zi[j] * yC[0]; }
|
||||
|
||||
FilterIRR(b, a, yC, ziChan2, yB2, finalStates);
|
||||
|
||||
|
||||
for (j = 0; j < dataSize + (2 * lengthEdgeTransients); ++j) { yC2[j] = yB2[(dataSize + (2 * lengthEdgeTransients)) - 1 - j]; }
|
||||
for (j = 0; j < dataSize; ++j) { dataFiltered[j] = yC2[j + lengthEdgeTransients]; }
|
||||
}
|
||||
|
||||
|
||||
bool CApplyTemporalFilter::initialize()
|
||||
{
|
||||
bool res = true;
|
||||
res &= ip_signalMatrix.initialize(getInputParameter(OVP_Algorithm_ApplyTemporalFilter_InputParameterId_SignalMatrix));
|
||||
res &= ip_filterCoefsMatrix.initialize(getInputParameter(OVP_Algorithm_ApplyTemporalFilter_InputParameterId_FilterCoefsMatrix));
|
||||
res &= op_signalMatrix.initialize(getOutputParameter(OVP_Algorithm_ApplyTemporalFilter_OutputParameterId_FilteredSignalMatrix));
|
||||
return res;
|
||||
}
|
||||
|
||||
bool CApplyTemporalFilter::uninitialize()
|
||||
{
|
||||
op_signalMatrix.uninitialize();
|
||||
ip_filterCoefsMatrix.uninitialize();
|
||||
ip_signalMatrix.uninitialize();
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
|
||||
bool CApplyTemporalFilter::process()
|
||||
{
|
||||
CMatrix* iMatrix = ip_signalMatrix;
|
||||
CMatrix* oMatrix = op_signalMatrix;
|
||||
|
||||
if (isInputTriggerActive(OVP_Algorithm_ApplyTemporalFilter_InputTriggerId_Initialize))
|
||||
{
|
||||
m_flagInitialize = true;
|
||||
|
||||
oMatrix->copyDescription(*iMatrix);
|
||||
|
||||
// dimension of input coef (numerator, denominator) filter
|
||||
const size_t filterCoefNumeratorDimSize = ip_filterCoefsMatrix->getDimensionSize(0);
|
||||
const size_t filterCoefDenominatorDimSize = ip_filterCoefsMatrix->getDimensionSize(0);
|
||||
|
||||
//coef filters vars
|
||||
CMatrix* filterCoefInputMatrix = ip_filterCoefsMatrix;
|
||||
double* filterCoefInput = filterCoefInputMatrix->getBuffer();
|
||||
|
||||
m_coefFilterDen = itpp::zeros(filterCoefDenominatorDimSize);
|
||||
m_coefFilterNum = itpp::zeros(filterCoefNumeratorDimSize);
|
||||
|
||||
|
||||
for (size_t i = 0; i < filterCoefNumeratorDimSize; ++i) { m_coefFilterNum[i] = filterCoefInput[i]; }
|
||||
|
||||
|
||||
for (size_t i = 0; i < filterCoefDenominatorDimSize; ++i) { m_coefFilterDen[i] = filterCoefInput[filterCoefNumeratorDimSize + i]; }
|
||||
}
|
||||
|
||||
// This mode is used when the consecutive input chunks are discontinuous in time
|
||||
if (isInputTriggerActive(OVP_Algorithm_ApplyTemporalFilter_InputTriggerId_ApplyFilter))
|
||||
{
|
||||
// signal input vars
|
||||
double* input = iMatrix->getBuffer();
|
||||
|
||||
// dimension of input signal buffer
|
||||
const size_t nDim = ip_signalMatrix->getDimensionCount();
|
||||
const size_t nChannels = ip_signalMatrix->getDimensionSize(0);
|
||||
const size_t nEpoch = ip_signalMatrix->getDimensionSize(1);
|
||||
|
||||
// signal output vars
|
||||
oMatrix->setDimensionCount(nDim);
|
||||
for (size_t i = 0; i < nDim; ++i) { oMatrix->setDimensionSize(i, ip_signalMatrix->getDimensionSize(i)); }
|
||||
double* filteredSignalMatrix = oMatrix->getBuffer();
|
||||
|
||||
itpp::vec y(nEpoch);
|
||||
itpp::vec x = itpp::zeros(nEpoch);
|
||||
|
||||
// test that Filtfilt() won't exceed the data array boundaries
|
||||
const size_t minSize = 3 * (m_coefFilterDen.size() - 1) + 1;
|
||||
if (nEpoch < minSize)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Data chunk size (" << nEpoch << ") "
|
||||
<< "is too short for the requirements of the filter (" << minSize << "). Please use a larger chunk size.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < nChannels; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < nEpoch; ++j) { x[int(j)] = double(input[i * nEpoch + j]); }
|
||||
|
||||
// --- Modif Manu
|
||||
Filtfilt(m_coefFilterNum, m_coefFilterDen, x, y);
|
||||
// --- Fin Modif Manu
|
||||
|
||||
for (size_t k = 0; k < nEpoch; ++k) { filteredSignalMatrix[i * nEpoch + k] = y[k]; }
|
||||
}
|
||||
}
|
||||
|
||||
// This mode is used when the consecutive input chunks are continuous in time
|
||||
if (isInputTriggerActive(OVP_Algorithm_ApplyTemporalFilter_InputTriggerId_ApplyFilterWithHistoric))
|
||||
{
|
||||
// signal input vars
|
||||
double* input = iMatrix->getBuffer();
|
||||
|
||||
// dimension of input signal biuffer
|
||||
const size_t nDim = ip_signalMatrix->getDimensionCount();
|
||||
const size_t nChannels = ip_signalMatrix->getDimensionSize(0);
|
||||
const size_t nEpoch = ip_signalMatrix->getDimensionSize(1);
|
||||
|
||||
// historic buffers
|
||||
if (m_flagInitialize)
|
||||
{
|
||||
// --- Modif Manu
|
||||
itpp::vec zi = itpp::zeros(int(ip_filterCoefsMatrix->getDimensionSize(0) - 1));
|
||||
ComputeFilterInitialCondition(m_coefFilterNum, m_coefFilterDen, zi);
|
||||
|
||||
m_currentStates.resize(nChannels);
|
||||
for (size_t i = 0; i < nChannels; ++i) { m_currentStates[i] = zi * double(input[i * nEpoch]); }
|
||||
// --- Fin Modif Manu
|
||||
|
||||
m_flagInitialize = false;
|
||||
}
|
||||
|
||||
// signal output vars
|
||||
oMatrix->setDimensionCount(nDim);
|
||||
for (size_t i = 0; i < nDim; ++i) { oMatrix->setDimensionSize(i, ip_signalMatrix->getDimensionSize(i)); }
|
||||
double* filteredSignalMatrix = oMatrix->getBuffer();
|
||||
|
||||
itpp::vec x = itpp::zeros(nEpoch);
|
||||
|
||||
itpp::vec y(nEpoch);
|
||||
//y = zeros(nEpoch);
|
||||
|
||||
for (size_t i = 0; i < nChannels; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < nEpoch; ++j) { x[j] = double(input[i * nEpoch + j]); }
|
||||
// --- Modif Manu
|
||||
y = itpp::zeros(nEpoch);
|
||||
FilterIRR(m_coefFilterNum, m_coefFilterDen, x, m_currentStates[i], y, m_currentStates[i]);
|
||||
// --- Fin Modif Manu
|
||||
|
||||
for (size_t k = 0; k < nEpoch; ++k) { filteredSignalMatrix[i * nEpoch + k] = y[k]; }
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyITPP
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyITPP
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <itpp/itstat.h>
|
||||
#include <itpp/itsignal.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
class CApplyTemporalFilter final : virtual public Toolkit::TAlgorithm<IAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TAlgorithm<IAlgorithm>, OVP_ClassId_Algorithm_ApplyTemporalFilter)
|
||||
|
||||
protected:
|
||||
|
||||
Kernel::TParameterHandler<CMatrix*> ip_signalMatrix;
|
||||
Kernel::TParameterHandler<CMatrix*> ip_filterCoefsMatrix;
|
||||
Kernel::TParameterHandler<CMatrix*> op_signalMatrix;
|
||||
|
||||
itpp::vec m_coefFilterDen;
|
||||
itpp::vec m_coefFilterNum;
|
||||
std::vector<itpp::vec> m_currentStates;
|
||||
|
||||
bool m_flagInitialize = false;
|
||||
};
|
||||
|
||||
class CApplyTemporalFilterDesc final : virtual public IAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Temporal Filter (INSERM contrib)"); }
|
||||
CString getAuthorName() const override { return CString("Guillaume Gibert"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INSERM/U821"); }
|
||||
CString getShortDescription() const override { return CString(""); }
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString("Algorithm/Signal processing/Filter"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_Algorithm_ApplyTemporalFilter; }
|
||||
IPluginObject* create() override { return new CApplyTemporalFilter(); }
|
||||
|
||||
bool getAlgorithmPrototype(Kernel::IAlgorithmProto& prototype) const override
|
||||
{
|
||||
prototype.addInputParameter(OVP_Algorithm_ApplyTemporalFilter_InputParameterId_SignalMatrix, "Signal matrix", Kernel::ParameterType_Matrix);
|
||||
prototype.addInputParameter(OVP_Algorithm_ApplyTemporalFilter_InputParameterId_FilterCoefsMatrix, "Filter coefficients matrix", Kernel::ParameterType_Matrix);
|
||||
prototype.addOutputParameter(OVP_Algorithm_ApplyTemporalFilter_OutputParameterId_FilteredSignalMatrix, "Filtered signal matrix", Kernel::ParameterType_Matrix);
|
||||
prototype.addInputTrigger(OVP_Algorithm_ApplyTemporalFilter_InputTriggerId_Initialize, "Initialize");
|
||||
prototype.addInputTrigger(OVP_Algorithm_ApplyTemporalFilter_InputTriggerId_ApplyFilter, "Apply filter");
|
||||
prototype.addInputTrigger(OVP_Algorithm_ApplyTemporalFilter_InputTriggerId_ApplyFilterWithHistoric, "Apply filter with historic");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IAlgorithmDesc, OVP_ClassId_Algorithm_ApplyTemporalFilterDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyITPP
|
||||
+639
@@ -0,0 +1,639 @@
|
||||
#if defined TARGET_HAS_ThirdPartyITPP
|
||||
|
||||
#include "ovpCComputeTemporalFilterCoefficients.h"
|
||||
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
// Add 2complexes
|
||||
void CComputeTemporalFilterCoefficients::addComplex(cmplex* a, cmplex* b, cmplex* c)
|
||||
{
|
||||
c->real = b->real + a->real;
|
||||
c->imag = b->imag + a->imag;
|
||||
}
|
||||
|
||||
// Substract 2 complex
|
||||
void CComputeTemporalFilterCoefficients::subComplex(cmplex* a, cmplex* b, cmplex* c)
|
||||
{
|
||||
c->real = b->real - a->real;
|
||||
c->imag = b->imag - a->imag;
|
||||
}
|
||||
|
||||
// Multiply 2 complexes
|
||||
void CComputeTemporalFilterCoefficients::mulComplex(cmplex* a, cmplex* b, cmplex* c)
|
||||
{
|
||||
const double y = b->real * a->real - b->imag * a->imag;
|
||||
c->imag = b->real * a->imag + b->imag * a->real;
|
||||
c->real = y;
|
||||
}
|
||||
|
||||
// Divide 2 complex numbers
|
||||
void CComputeTemporalFilterCoefficients::divComplex(cmplex* a, cmplex* b, cmplex* c) const
|
||||
{
|
||||
const double y = a->real * a->real + a->imag * a->imag;
|
||||
const double p = b->real * a->real + b->imag * a->imag;
|
||||
const double q = b->imag * a->real - b->real * a->imag;
|
||||
|
||||
if (y < 1.0)
|
||||
{
|
||||
const double w = MAXNUM * y;
|
||||
if ((fabs(p) > w) || (fabs(q) > w) || (y == 0.0))
|
||||
{
|
||||
c->real = MAXNUM;
|
||||
c->imag = MAXNUM;
|
||||
std::cout << "divCOMPLEX: OVERFLOW" << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
c->real = p / y;
|
||||
c->imag = q / y;
|
||||
}
|
||||
|
||||
// Compute abs of a complex
|
||||
double CComputeTemporalFilterCoefficients::absComplex(cmplex* z) const
|
||||
{
|
||||
int ex, ey;
|
||||
|
||||
const double re = fabs(z->real);
|
||||
const double im = fabs(z->imag);
|
||||
|
||||
if (re == 0.0) { return (im); }
|
||||
if (im == 0.0) { return (re); }
|
||||
|
||||
// Get the exponents of the numbers
|
||||
frexp(re, &ex);
|
||||
frexp(im, &ey);
|
||||
|
||||
// Check if one number is tiny compared to the other
|
||||
int e = ex - ey;
|
||||
if (e > PREC) { return (re); }
|
||||
if (e < -PREC) { return (im); }
|
||||
|
||||
// Find approximate exponent e of the geometric mean.
|
||||
e = (ex + ey) >> 1;
|
||||
|
||||
// Rescale so mean is about 1
|
||||
const double x = ldexp(re, -e);
|
||||
double y = ldexp(im, -e);
|
||||
|
||||
// Hypotenuse of the right triangle
|
||||
double b = sqrt(x * x + y * y);
|
||||
|
||||
// Compute the exponent of the answer.
|
||||
y = frexp(b, &ey);
|
||||
ey = e + ey;
|
||||
|
||||
// Check it for overflow and underflow.
|
||||
if (ey > MAXEXP)
|
||||
{
|
||||
std::cout << "absCOMPLEX: OVERFLOW" << std::endl;
|
||||
return (std::numeric_limits<double>::infinity());
|
||||
}
|
||||
if (ey < MINEXP) { return (0.0); }
|
||||
|
||||
// Undo the scaling
|
||||
b = ldexp(b, e);
|
||||
return (b);
|
||||
}
|
||||
|
||||
// Compute sqrt of a complex number
|
||||
void CComputeTemporalFilterCoefficients::sqrtComplex(cmplex* z, cmplex* w) const
|
||||
{
|
||||
cmplex q, s;
|
||||
double r, t;
|
||||
|
||||
const double x = z->real;
|
||||
const double y = z->imag;
|
||||
|
||||
if (y == 0.0)
|
||||
{
|
||||
if (x < 0.0)
|
||||
{
|
||||
w->real = 0.0;
|
||||
w->imag = sqrt(-x);
|
||||
return;
|
||||
}
|
||||
w->real = sqrt(x);
|
||||
w->imag = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (x == 0.0)
|
||||
{
|
||||
r = fabs(y);
|
||||
r = sqrt(0.5 * r);
|
||||
if (y > 0) { w->real = r; }
|
||||
else { w->real = -r; }
|
||||
w->imag = r;
|
||||
return;
|
||||
}
|
||||
|
||||
// Approximate sqrt(x^2+y^2) - x = y^2/2x - y^4/24x^3 + ... .
|
||||
// The relative error in the first term is approximately y^2/12x^2 .
|
||||
if ((fabs(y) < 2.e-4 * fabs(x)) && (x > 0)) { t = 0.25 * y * (y / x); }
|
||||
else
|
||||
{
|
||||
r = absComplex(z);
|
||||
t = 0.5 * (r - x);
|
||||
}
|
||||
r = sqrt(t);
|
||||
q.imag = r;
|
||||
q.real = y / (2.0 * r);
|
||||
// Heron iteration in complex arithmetic
|
||||
divComplex(&q, z, &s);
|
||||
addComplex(&q, &s, w);
|
||||
w->real *= 0.5;
|
||||
w->imag *= 0.5;
|
||||
}
|
||||
|
||||
// compute s plane poles and zeros
|
||||
void CComputeTemporalFilterCoefficients::findSPlanePolesAndZeros()
|
||||
{
|
||||
m_nPoles = (m_filterOrder + 1) / 2;
|
||||
m_nZeros = 0;
|
||||
m_zs = itpp::zeros(m_arraySize);
|
||||
|
||||
double dm;
|
||||
size_t ii = 0;
|
||||
double db;
|
||||
|
||||
if (m_filterMethod == EFilterMethod::Butterworth)//poles equally spaced around the unit circle
|
||||
{
|
||||
if (m_filterOrder & 1) { dm = 0.0; }
|
||||
else { dm = itpp::pi / (2.0 * double(m_filterOrder)); }
|
||||
|
||||
for (size_t i = 0; i < m_nPoles; ++i)// poles
|
||||
{
|
||||
const size_t lr = i + i;
|
||||
m_zs[lr] = -cos(dm);
|
||||
m_zs[lr + 1] = sin(dm);
|
||||
dm += itpp::pi / double(m_filterOrder);
|
||||
}
|
||||
|
||||
if (m_filterType == EFilterType::HighPass || m_filterType == EFilterType::BandStop) // high pass or band reject
|
||||
{
|
||||
// map s => 1/s
|
||||
for (size_t j = 0; j < m_nPoles; ++j)
|
||||
{
|
||||
const size_t ir = j + j;
|
||||
ii = ir + 1;
|
||||
db = m_zs[ir] * m_zs[ir] + m_zs[ii] * m_zs[ii];
|
||||
m_zs[ir] = m_zs[ir] / db;
|
||||
m_zs[ii] = m_zs[ii] / db;
|
||||
}
|
||||
|
||||
// The zeros at infinity map to the origin.
|
||||
m_nZeros = m_nPoles;
|
||||
if (m_filterType == EFilterType::BandStop) { m_nZeros += m_filterOrder / 2; }
|
||||
for (size_t j = 0; j < m_nZeros; ++j)
|
||||
{
|
||||
const size_t ir = ii + 1;
|
||||
ii = ir + 1;
|
||||
m_zs[ir] = 0.0;
|
||||
m_zs[ii] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_filterMethod == EFilterMethod::Chebyshev)
|
||||
{
|
||||
//For Chebyshev, find radii of two Butterworth circles (See Gold & Rader, page 60)
|
||||
m_rho = (m_phi - 1.0) * (m_phi + 1); // m_rho = m_eps^2 = {sqrt(1+m_eps^2)}^2 - 1
|
||||
m_eps = sqrt(m_rho); // sqrt( 1 + 1/m_eps^2 ) + 1/m_eps = {sqrt(1 + m_eps^2) + 1} / m_eps
|
||||
m_phi = (m_phi + 1.0) / m_eps;
|
||||
m_phi = pow(m_phi, double(1.0) / m_filterOrder); // raise to the 1/n power
|
||||
db = 0.5 * (m_phi + 1.0 / m_phi); // y coordinates are on this circle
|
||||
const double da = 0.5 * (m_phi - 1.0 / m_phi); // x coordinates are on this circle
|
||||
if (m_filterOrder & 1) { dm = 0.0; }
|
||||
else { dm = itpp::pi / (2.0 * double(m_filterOrder)); }
|
||||
|
||||
for (size_t i = 0; i < m_nPoles; ++i)// poles
|
||||
{
|
||||
const size_t lr = i + i;
|
||||
m_zs[lr] = -da * cos(dm);
|
||||
m_zs[lr + 1] = db * sin(dm);
|
||||
dm += itpp::pi / double(m_filterOrder);
|
||||
}
|
||||
|
||||
if (m_filterType == EFilterType::HighPass || m_filterType == EFilterType::BandStop)// high pass or band reject
|
||||
{
|
||||
// map s => 1/s
|
||||
for (size_t j = 0; j < m_nPoles; ++j)
|
||||
{
|
||||
const size_t ir = j + j;
|
||||
ii = ir + 1;
|
||||
db = m_zs[ir] * m_zs[ir] + m_zs[ii] * m_zs[ii];
|
||||
m_zs[ir] = m_zs[ir] / db;
|
||||
m_zs[ii] = m_zs[ii] / db;
|
||||
}
|
||||
// The zeros at infinity map to the origin.
|
||||
m_nZeros = m_nPoles;
|
||||
if (m_filterType == EFilterType::BandStop) { m_nZeros += m_filterOrder / 2; }
|
||||
|
||||
for (size_t j = 0; j < m_nZeros; ++j)
|
||||
{
|
||||
const size_t ir = ii + 1;
|
||||
ii = ir + 1;
|
||||
m_zs[ir] = 0.0;
|
||||
m_zs[ii] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//convert s plane poles and zeros to the z plane.
|
||||
void CComputeTemporalFilterCoefficients::convertSPlanePolesAndZerosToZPlane()
|
||||
{
|
||||
// Vars
|
||||
cmplex r, cnum, cden, cwc, ca, cb, b4Ac;
|
||||
cmplex cone = { 1.0, 0.0 };
|
||||
cmplex* z = new cmplex[m_arraySize];
|
||||
double* pp = new double[m_arraySize];
|
||||
double* y = new double[m_arraySize];
|
||||
double* aa = new double[m_arraySize];
|
||||
|
||||
double c = 0.0, a = 0.0, b = 0.0, pn = 0.0, an = 0.0, gam = 0.0, ai = 0.0, cng = 0.0, gain = 0.0;
|
||||
size_t nc = 0, jt = 0, ii = 0, ir = 0, jj = 0, jh = 0, jl = 0, mh = 0;
|
||||
|
||||
c = m_tanAng;
|
||||
|
||||
for (size_t i = 0; i < m_arraySize; ++i)
|
||||
{
|
||||
z[i].real = 0.0;
|
||||
z[i].imag = 0.0;
|
||||
}
|
||||
|
||||
nc = m_nPoles;
|
||||
jt = -1;
|
||||
ii = -1;
|
||||
|
||||
for (size_t icnt = 0; icnt < 2; ++icnt)
|
||||
{
|
||||
do
|
||||
{
|
||||
ir = ii + 1;
|
||||
ii = ir + 1;
|
||||
|
||||
r.real = m_zs[ir];
|
||||
r.imag = m_zs[ii];
|
||||
|
||||
if (m_filterType == EFilterType::LowPass || m_filterType == EFilterType::HighPass)
|
||||
{
|
||||
// Substitute s - r = s/wc - r = (1/wc)(z-1)/(z+1) - r
|
||||
//
|
||||
// 1 1 - r wc ( 1 + r wc )
|
||||
// = --- -------- ( z - -------- )
|
||||
// z+1 wc ( 1 - r wc )
|
||||
//
|
||||
// giving the root in the z plane.
|
||||
cnum.real = 1 + c * r.real;
|
||||
cnum.imag = c * r.imag;
|
||||
cden.real = 1 - c * r.real;
|
||||
cden.imag = -c * r.imag;
|
||||
jt += 1;
|
||||
divComplex(&cden, &cnum, &z[jt]);
|
||||
|
||||
if (r.imag != 0.0)
|
||||
{
|
||||
// fill in complex conjugate root
|
||||
jt += 1;
|
||||
z[jt].real = z[jt - 1].real;
|
||||
z[jt].imag = -z[jt - 1].imag;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_filterType == EFilterType::BandPass || m_filterType == EFilterType::BandStop)
|
||||
{
|
||||
// Substitute s - r => s/wc - r
|
||||
//
|
||||
// z^2 - 2 z cgam + 1
|
||||
// => ------------------ - r
|
||||
// (z^2 + 1) wc
|
||||
//
|
||||
// 1
|
||||
// = ------------ [ (1 - r wc) z^2 - 2 cgam z + 1 + r wc ]
|
||||
// (z^2 + 1) wc
|
||||
//
|
||||
// and solve for the roots in the z plane.
|
||||
|
||||
if (m_filterMethod == EFilterMethod::Chebyshev) { cwc.real = m_cbp; }
|
||||
else { cwc.real = m_tanAng; }
|
||||
cwc.imag = 0.0;
|
||||
|
||||
// r * wc //
|
||||
mulComplex(&r, &cwc, &cnum);
|
||||
// a = 1 - r wc //
|
||||
subComplex(&cnum, &cone, &ca);
|
||||
// 1 - (r wc)^2 //
|
||||
mulComplex(&cnum, &cnum, &b4Ac);
|
||||
subComplex(&b4Ac, &cone, &b4Ac);
|
||||
// 4ac //
|
||||
b4Ac.real *= 4.0;
|
||||
b4Ac.imag *= 4.0;
|
||||
// b //
|
||||
cb.real = -2.0 * m_cosGam;
|
||||
cb.imag = 0.0;
|
||||
// b^2 //
|
||||
mulComplex(&cb, &cb, &cnum);
|
||||
// b^2 - 4ac//
|
||||
subComplex(&b4Ac, &cnum, &b4Ac);
|
||||
// sqrt() //
|
||||
sqrtComplex(&b4Ac, &b4Ac);
|
||||
// -b //
|
||||
cb.real = -cb.real;
|
||||
cb.imag = -cb.imag;
|
||||
// 2a //
|
||||
ca.real *= 2.0;
|
||||
ca.imag *= 2.0;
|
||||
// -b +sqrt(b^2-4ac) //
|
||||
addComplex(&b4Ac, &cb, &cnum);
|
||||
// ... /2a //
|
||||
divComplex(&ca, &cnum, &cnum);
|
||||
jt += 1;
|
||||
|
||||
z[jt].real = cnum.real;
|
||||
z[jt].imag = cnum.imag;
|
||||
|
||||
if (cnum.imag != 0.0)
|
||||
{
|
||||
jt += 1;
|
||||
z[jt].real = cnum.real;
|
||||
z[jt].imag = -cnum.imag;
|
||||
}
|
||||
if ((r.imag != 0.0) || cnum.imag == 0.0)
|
||||
{
|
||||
// -b - sqrt( b^2 - 4ac) //
|
||||
subComplex(&b4Ac, &cb, &cnum);
|
||||
// ... /2a //
|
||||
divComplex(&ca, &cnum, &cnum);
|
||||
|
||||
jt += 1;
|
||||
z[jt].real = cnum.real;
|
||||
z[jt].imag = cnum.imag;
|
||||
|
||||
if (cnum.imag != 0.0)
|
||||
{
|
||||
jt += 1;
|
||||
z[jt].real = cnum.real;
|
||||
z[jt].imag = -cnum.imag;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (--nc > 0);
|
||||
|
||||
if (icnt == 0)
|
||||
{
|
||||
m_zOrd = jt + 1;
|
||||
if (m_nZeros <= 0) { icnt = 2; }
|
||||
}
|
||||
nc = m_nZeros;
|
||||
}
|
||||
|
||||
// Generate the remaining zeros
|
||||
while (2 * m_zOrd - 1 > jt)
|
||||
{
|
||||
if (m_filterType != EFilterType::HighPass)
|
||||
{
|
||||
jt += 1;
|
||||
z[jt].real = -1.0;
|
||||
z[jt].imag = 0.0;
|
||||
}
|
||||
|
||||
if (m_filterType == EFilterType::BandPass || m_filterType == EFilterType::HighPass)
|
||||
{
|
||||
jt += 1;
|
||||
z[jt].real = 1.0;
|
||||
z[jt].imag = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Expand the poles and zeros into numerator and denominator polynomials
|
||||
for (size_t j = 0; j < m_arraySize; ++j) { aa[j] = 0.0; }
|
||||
for (size_t icnt = 0; icnt < 2; ++icnt)
|
||||
{
|
||||
for (size_t j = 0; j < m_arraySize; ++j)
|
||||
{
|
||||
pp[j] = 0.0;
|
||||
y[j] = 0.0;
|
||||
}
|
||||
pp[0] = 1.0;
|
||||
|
||||
for (size_t j = 0; j < m_zOrd; ++j)
|
||||
{
|
||||
jj = j;
|
||||
if (icnt) { jj += m_zOrd; }
|
||||
|
||||
a = z[jj].real;
|
||||
b = z[jj].imag;
|
||||
for (size_t k = 0; k <= j; ++k)
|
||||
{
|
||||
jh = j - k;
|
||||
pp[jh + 1] = pp[jh + 1] - a * pp[jh] + b * y[jh];
|
||||
y[jh + 1] = y[jh + 1] - b * pp[jh] - a * y[jh];
|
||||
}
|
||||
}
|
||||
|
||||
if (icnt == 0) { for (size_t j = 0; j <= m_zOrd; ++j) { aa[j] = pp[j]; } }
|
||||
}
|
||||
|
||||
// Scale factors of the pole and zero polynomials
|
||||
if (m_filterType == EFilterType::HighPass) { a = -1.0; }
|
||||
else { a = 1.0; }
|
||||
|
||||
if (m_filterType == EFilterType::HighPass || m_filterType == EFilterType::LowPass || m_filterType ==
|
||||
EFilterType::BandStop)
|
||||
{
|
||||
pn = 1.0;
|
||||
an = 1.0;
|
||||
for (size_t j = 1; j <= m_zOrd; ++j)
|
||||
{
|
||||
pn = a * pn + pp[j];
|
||||
an = a * an + aa[j];
|
||||
}
|
||||
}
|
||||
|
||||
if (m_filterType == EFilterType::BandPass)
|
||||
{
|
||||
gam = itpp::pi / 2.0 - asin(m_cosGam); // = acos( cgam ) //
|
||||
mh = m_zOrd / 2;
|
||||
pn = pp[mh];
|
||||
an = aa[mh];
|
||||
ai = 0.0;
|
||||
if (mh > ((m_zOrd / 4) * 2))
|
||||
{
|
||||
ai = 1.0;
|
||||
pn = 0.0;
|
||||
an = 0.0;
|
||||
}
|
||||
for (size_t j = 1; j <= mh; ++j)
|
||||
{
|
||||
a = gam * j - ai * itpp::pi / 2.0;
|
||||
cng = cos(a);
|
||||
jh = mh + j;
|
||||
jl = mh - j;
|
||||
pn = pn + cng * (pp[jh] + (1.0 - 2.0 * ai) * pp[jl]);
|
||||
an = an + cng * (aa[jh] + (1.0 - 2.0 * ai) * aa[jl]);
|
||||
}
|
||||
}
|
||||
|
||||
gain = an / (pn * m_scale);
|
||||
if (pn == 0.0) { gain = 1.0; }
|
||||
|
||||
for (size_t j = 0; j <= m_zOrd; ++j) { pp[j] = gain * pp[j]; }
|
||||
|
||||
for (size_t j = 0; j <= m_zOrd; ++j)
|
||||
{
|
||||
m_coefFilterDen[j] = pp[j];
|
||||
m_coefFilterNum[j] = aa[j];
|
||||
}
|
||||
|
||||
delete [] z;
|
||||
delete [] pp;
|
||||
delete [] y;
|
||||
delete [] aa;
|
||||
}
|
||||
|
||||
bool CComputeTemporalFilterCoefficients::initialize()
|
||||
{
|
||||
ip_sampling.initialize(getInputParameter(OVP_Algorithm_ComputeTemporalFilterCoefs_InputParameterId_Sampling));
|
||||
ip_filterMethod.initialize(getInputParameter(OVP_Algorithm_ComputeTemporalFilterCoefs_InputParameterId_FilterMethod));
|
||||
ip_filterType.initialize(getInputParameter(OVP_Algorithm_ComputeTemporalFilterCoefs_InputParameterId_FilterType));
|
||||
ip_filterOrder.initialize(getInputParameter(OVP_Algorithm_ComputeTemporalFilterCoefs_InputParameterId_FilterOrder));
|
||||
ip_lowCutFrequency.initialize(getInputParameter(OVP_Algorithm_ComputeTemporalFilterCoefs_InputParameterId_LowCutFrequency));
|
||||
ip_highCutFrequency.initialize(getInputParameter(OVP_Algorithm_ComputeTemporalFilterCoefs_InputParameterId_HighCutFrequency));
|
||||
ip_bandPassRipple.initialize(getInputParameter(OVP_Algorithm_ComputeTemporalFilterCoefs_InputParameterId_BandPassRipple));
|
||||
|
||||
op_matrix.initialize(getOutputParameter(OVP_Algorithm_ComputeTemporalFilterCoefs_OutputParameterId_Matrix));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CComputeTemporalFilterCoefficients::uninitialize()
|
||||
{
|
||||
op_matrix.uninitialize();
|
||||
|
||||
ip_bandPassRipple.uninitialize();
|
||||
ip_highCutFrequency.uninitialize();
|
||||
ip_lowCutFrequency.uninitialize();
|
||||
ip_filterOrder.uninitialize();
|
||||
ip_filterType.uninitialize();
|
||||
ip_filterMethod.uninitialize();
|
||||
ip_sampling.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ________________________________________________________________________________________________________________
|
||||
//
|
||||
|
||||
bool CComputeTemporalFilterCoefficients::process()
|
||||
{
|
||||
if (isInputTriggerActive(OVP_Algorithm_ComputeTemporalFilterCoefs_InputTriggerId_Initialize))
|
||||
{
|
||||
m_sampling = size_t(ip_sampling);
|
||||
m_filterMethod = EFilterMethod(uint64_t(ip_filterMethod));
|
||||
m_filterType = EFilterType(uint64_t(ip_filterType));
|
||||
m_filterOrder = size_t(ip_filterOrder);
|
||||
m_lowPassBandEdge = ip_lowCutFrequency;
|
||||
m_highPassBandEdge = ip_highCutFrequency;
|
||||
m_passBandRipple = ip_bandPassRipple;
|
||||
|
||||
m_arraySize = 4 * m_filterOrder; // Maximum size of array involved in computation
|
||||
}
|
||||
|
||||
if (isInputTriggerActive(OVP_Algorithm_ComputeTemporalFilterCoefs_InputTriggerId_ComputeCoefs))
|
||||
{
|
||||
if (m_filterMethod == EFilterMethod::Butterworth || m_filterMethod == EFilterMethod::Chebyshev)
|
||||
{
|
||||
if (m_filterType == EFilterType::LowPass || m_filterType == EFilterType::HighPass)
|
||||
{
|
||||
m_dimSize = m_filterOrder + 1;
|
||||
m_coefFilterDen = itpp::zeros(m_dimSize);
|
||||
m_coefFilterNum = itpp::zeros(m_dimSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dimSize = 2 * m_filterOrder + 1;
|
||||
m_coefFilterDen = itpp::zeros(m_dimSize);
|
||||
m_coefFilterNum = itpp::zeros(m_dimSize);
|
||||
}
|
||||
|
||||
if (m_filterMethod == EFilterMethod::Chebyshev)
|
||||
{
|
||||
// For Chebyshev filter, ripples go from 1.0 to 1/sqrt(1+m_eps^2)
|
||||
m_phi = exp(0.5 * m_passBandRipple / (10.0 / log(10.0)));
|
||||
|
||||
if ((m_filterOrder & 1) == 0) { m_scale = m_phi; }
|
||||
else { m_scale = 1.0; }
|
||||
}
|
||||
|
||||
m_nyquist = m_sampling / 2;
|
||||
|
||||
//locate edges
|
||||
if (m_filterType == EFilterType::LowPass) { m_lowPassBandEdge = 0.0; }
|
||||
|
||||
//local variables
|
||||
double bandWidth, highFrequencyEdge;
|
||||
|
||||
if (m_filterType == EFilterType::HighPass)
|
||||
{
|
||||
bandWidth = m_highPassBandEdge;
|
||||
highFrequencyEdge = double(m_nyquist);
|
||||
}
|
||||
else
|
||||
{
|
||||
bandWidth = m_highPassBandEdge - m_lowPassBandEdge;
|
||||
highFrequencyEdge = m_highPassBandEdge;
|
||||
}
|
||||
|
||||
//convert to Frequency correspondence for bilinear transformation
|
||||
// Wanalog = tan( 2 pi Fdigital T / 2 )
|
||||
// where T = 1/fs
|
||||
const double ang = double(bandWidth) * itpp::pi / double(m_sampling);
|
||||
const double cosAng = cos(ang);
|
||||
m_tanAng = sin(ang) / cosAng; // Wanalog
|
||||
|
||||
// Transformation from low-pass to band-pass critical frequencies
|
||||
// Center frequency
|
||||
// cos( 1/2 (Whigh+Wlow) T )
|
||||
// cos( Wcenter T ) = -------------------------
|
||||
// cos( 1/2 (Whigh-Wlow) T )
|
||||
//
|
||||
// Band edges
|
||||
// cos( Wcenter T) - cos( Wdigital T )
|
||||
// Wanalog = -----------------------------------
|
||||
// sin( Wdigital T )
|
||||
|
||||
highFrequencyEdge = itpp::pi * (highFrequencyEdge + m_lowPassBandEdge) / double(m_sampling);
|
||||
m_cosGam = cos(highFrequencyEdge) / cosAng;
|
||||
highFrequencyEdge = 2.0 * itpp::pi * m_highPassBandEdge / double(m_sampling);
|
||||
m_cbp = (m_cosGam - cos(highFrequencyEdge)) / sin(highFrequencyEdge);
|
||||
|
||||
if (m_filterMethod == EFilterMethod::Butterworth) { m_scale = 1.0; }
|
||||
|
||||
findSPlanePolesAndZeros();
|
||||
convertSPlanePolesAndZerosToZPlane();
|
||||
}
|
||||
|
||||
CMatrix* oMatrix = op_matrix;
|
||||
oMatrix->resize(m_dimSize, 2); // Push m_coefFilterDen and m_coefFilterNum as two column vectors m_dimension rows
|
||||
|
||||
double* filterCoefMatrix = oMatrix->getBuffer();
|
||||
for (size_t i = 0; i < m_dimSize; ++i)
|
||||
{
|
||||
filterCoefMatrix[i] = m_coefFilterDen[i];
|
||||
filterCoefMatrix[m_dimSize + i] = m_coefFilterNum[i];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyITPP
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
#pragma once
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyITPP
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <itpp/itstat.h>
|
||||
#include <itpp/itsignal.h>
|
||||
|
||||
#define PREC 27
|
||||
#define MAXEXP 1024
|
||||
#define MINEXP -1077
|
||||
#define MAXNUM 1.79769313486231570815E308
|
||||
|
||||
typedef struct
|
||||
{
|
||||
double real;
|
||||
double imag;
|
||||
} cmplex;
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
class CComputeTemporalFilterCoefficients : virtual public Toolkit::TAlgorithm<IAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TAlgorithm<IAlgorithm>, OVP_ClassId_Algorithm_ComputeTemporalFilterCoefs)
|
||||
|
||||
// Functions for Butterworth and Chebyshev filters
|
||||
void findSPlanePolesAndZeros();
|
||||
void convertSPlanePolesAndZerosToZPlane();
|
||||
// Functions for Complex arithmetic
|
||||
double absComplex(cmplex* z) const;
|
||||
void divComplex(cmplex* a, cmplex* b, cmplex* c) const;
|
||||
void sqrtComplex(cmplex* z, cmplex* w) const;
|
||||
static void addComplex(cmplex* a, cmplex* b, cmplex* c);
|
||||
static void mulComplex(cmplex* a, cmplex* b, cmplex* c);
|
||||
static void subComplex(cmplex* a, cmplex* b, cmplex* c);
|
||||
|
||||
protected:
|
||||
|
||||
Kernel::TParameterHandler<uint64_t> ip_sampling;
|
||||
Kernel::TParameterHandler<uint64_t> ip_filterMethod;
|
||||
Kernel::TParameterHandler<uint64_t> ip_filterType;
|
||||
Kernel::TParameterHandler<uint64_t> ip_filterOrder;
|
||||
Kernel::TParameterHandler<double> ip_lowCutFrequency;
|
||||
Kernel::TParameterHandler<double> ip_highCutFrequency;
|
||||
Kernel::TParameterHandler<double> ip_bandPassRipple;
|
||||
|
||||
Kernel::TParameterHandler<CMatrix*> op_matrix;
|
||||
|
||||
size_t m_filterOrder = 0;
|
||||
EFilterMethod m_filterMethod = EFilterMethod::Butterworth;
|
||||
EFilterType m_filterType = EFilterType::BandPass;
|
||||
double m_lowPassBandEdge = 0;
|
||||
double m_highPassBandEdge = 0;
|
||||
double m_passBandRipple = 0;
|
||||
size_t m_arraySize = 0;
|
||||
itpp::vec m_coefFilterNum;
|
||||
itpp::vec m_coefFilterDen;
|
||||
double m_phi = 0;
|
||||
double m_scale = 0;
|
||||
double m_tanAng = 0;
|
||||
double m_cosGam = 0;
|
||||
double m_cbp = 0;
|
||||
|
||||
size_t m_sampling = 0;
|
||||
size_t m_nyquist = 0;
|
||||
|
||||
size_t m_nPoles = 0;
|
||||
size_t m_nZeros = 0;
|
||||
|
||||
itpp::vec m_zs;
|
||||
size_t m_zOrd = 0;
|
||||
|
||||
double m_rho = 0;
|
||||
double m_eps = 0;
|
||||
|
||||
size_t m_dimSize = 0;
|
||||
};
|
||||
|
||||
class CComputeTemporalFilterCoefficientsDesc final : virtual public IAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Compute Filter Coefficients"); }
|
||||
CString getAuthorName() const override { return CString("Guillaume Gibert"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INSERM/U821"); }
|
||||
CString getShortDescription() const override { return CString(""); }
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString("Algorithm/Signal processing/Filter"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_Algorithm_ComputeTemporalFilterCoefs; }
|
||||
IPluginObject* create() override { return new CComputeTemporalFilterCoefficients(); }
|
||||
|
||||
bool getAlgorithmPrototype(Kernel::IAlgorithmProto& prototype) const override
|
||||
{
|
||||
prototype.addInputParameter(OVP_Algorithm_ComputeTemporalFilterCoefs_InputParameterId_Sampling, "Sampling frequency", Kernel::ParameterType_UInteger);
|
||||
prototype.addInputParameter(OVP_Algorithm_ComputeTemporalFilterCoefs_InputParameterId_FilterMethod, "Filter method", Kernel::ParameterType_UInteger);
|
||||
prototype.addInputParameter(OVP_Algorithm_ComputeTemporalFilterCoefs_InputParameterId_FilterType, "Filter type", Kernel::ParameterType_UInteger);
|
||||
prototype.addInputParameter(OVP_Algorithm_ComputeTemporalFilterCoefs_InputParameterId_FilterOrder, "Filter order", Kernel::ParameterType_UInteger);
|
||||
prototype.addInputParameter(OVP_Algorithm_ComputeTemporalFilterCoefs_InputParameterId_LowCutFrequency, "Low cut frequency",
|
||||
Kernel::ParameterType_Float);
|
||||
prototype.addInputParameter(OVP_Algorithm_ComputeTemporalFilterCoefs_InputParameterId_HighCutFrequency, "High cut frequency",
|
||||
Kernel::ParameterType_Float);
|
||||
prototype.addInputParameter(OVP_Algorithm_ComputeTemporalFilterCoefs_InputParameterId_BandPassRipple, "Band pass ripple", Kernel::ParameterType_Float);
|
||||
prototype.addOutputParameter(OVP_Algorithm_ComputeTemporalFilterCoefs_OutputParameterId_Matrix, "Matrix", Kernel::ParameterType_Matrix);
|
||||
prototype.addInputTrigger(OVP_Algorithm_ComputeTemporalFilterCoefs_InputTriggerId_Initialize, "Initialize");
|
||||
prototype.addInputTrigger(OVP_Algorithm_ComputeTemporalFilterCoefs_InputTriggerId_ComputeCoefs, "Compute coefficients");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IAlgorithmDesc, OVP_ClassId_Algorithm_ComputeTemporalFilterCoefsDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyITPP
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
#include "ovpCDetectingMinMax.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
// ________________________________________________________________________________________________________________
|
||||
//
|
||||
|
||||
bool CDetectingMinMax::initialize()
|
||||
{
|
||||
ip_signalMatrix.initialize(getInputParameter(OVP_Algorithm_DetectingMinMax_InputParameterId_SignalMatrix));
|
||||
ip_sampling.initialize(getInputParameter(OVP_Algorithm_DetectingMinMax_InputParameterId_Sampling));
|
||||
ip_timeWindowStart.initialize(getInputParameter(OVP_Algorithm_DetectingMinMax_InputParameterId_TimeWindowStart));
|
||||
ip_timeWindowEnd.initialize(getInputParameter(OVP_Algorithm_DetectingMinMax_InputParameterId_TimeWindowEnd));
|
||||
|
||||
op_signalMatrix.initialize(getOutputParameter(OVP_Algorithm_DetectingMinMax_OutputParameterId_SignalMatrix));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDetectingMinMax::uninitialize()
|
||||
{
|
||||
op_signalMatrix.uninitialize();
|
||||
|
||||
ip_timeWindowEnd.uninitialize();
|
||||
ip_timeWindowStart.uninitialize();
|
||||
ip_sampling.uninitialize();
|
||||
ip_signalMatrix.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ________________________________________________________________________________________________________________
|
||||
//
|
||||
|
||||
bool CDetectingMinMax::process()
|
||||
{
|
||||
// signal input vars
|
||||
CMatrix* iMatrix = ip_signalMatrix;
|
||||
double* ibuffer = iMatrix->getBuffer();
|
||||
|
||||
// signal output vars
|
||||
CMatrix* oMatrix = op_signalMatrix;
|
||||
oMatrix->resize(1, 1);
|
||||
|
||||
double* oBuffer = oMatrix->getBuffer();
|
||||
|
||||
if (isInputTriggerActive(OVP_Algorithm_DetectingMinMax_InputTriggerId_Initialize)) { }
|
||||
|
||||
if (isInputTriggerActive(OVP_Algorithm_DetectingMinMax_InputTriggerId_DetectsMin))
|
||||
{
|
||||
// dimension of input signal biuffer
|
||||
const size_t nChannel = ip_signalMatrix->getDimensionSize(0);
|
||||
const size_t EpochSize = ip_signalMatrix->getDimensionSize(1);
|
||||
|
||||
// Must be changed
|
||||
double minValue = 1E10;
|
||||
|
||||
for (size_t i = 0; i < nChannel; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < EpochSize; ++j) { if (ibuffer[i * EpochSize + j] < minValue) { minValue = ibuffer[i * EpochSize + j]; } }
|
||||
}
|
||||
oBuffer[0] = minValue;
|
||||
}
|
||||
|
||||
if (isInputTriggerActive(OVP_Algorithm_DetectingMinMax_InputTriggerId_DetectsMax))
|
||||
{
|
||||
// dimension of input signal biuffer
|
||||
const size_t nChannels = ip_signalMatrix->getDimensionSize(0);
|
||||
const size_t epochSize = ip_signalMatrix->getDimensionSize(1);
|
||||
|
||||
// Must be changed
|
||||
double maxValue = -1E10;
|
||||
|
||||
for (size_t i = 0; i < nChannels; ++i)
|
||||
{
|
||||
const uint64_t start = uint64_t(floor(ip_timeWindowStart / 1000. * ip_sampling));
|
||||
const uint64_t stop = uint64_t(floor(ip_timeWindowEnd / 1000. * ip_sampling));
|
||||
for (uint64_t j = start; j < stop; ++j) { if (ibuffer[i * epochSize + j] > maxValue) { maxValue = ibuffer[i * epochSize + j]; } }
|
||||
}
|
||||
oBuffer[0] = maxValue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
class CDetectingMinMax final : virtual public Toolkit::TAlgorithm<IAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TAlgorithm<IAlgorithm>, OVP_ClassId_Algorithm_DetectingMinMax)
|
||||
|
||||
protected:
|
||||
|
||||
Kernel::TParameterHandler<CMatrix*> ip_signalMatrix;
|
||||
Kernel::TParameterHandler<uint64_t> ip_sampling;
|
||||
Kernel::TParameterHandler<double> ip_timeWindowStart;
|
||||
Kernel::TParameterHandler<double> ip_timeWindowEnd;
|
||||
|
||||
Kernel::TParameterHandler<CMatrix*> op_signalMatrix;
|
||||
};
|
||||
|
||||
class CDetectingMinMaxDesc final : virtual public IAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Detects Min or Max of input buffer"); }
|
||||
CString getAuthorName() const override { return CString("Guillaume Gibert"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INSERM/U821"); }
|
||||
CString getShortDescription() const override { return CString(""); }
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString("Algorithm/Signal processing/Basic"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_Algorithm_DetectingMinMax; }
|
||||
IPluginObject* create() override { return new CDetectingMinMax(); }
|
||||
|
||||
bool getAlgorithmPrototype(Kernel::IAlgorithmProto& prototype) const override
|
||||
{
|
||||
prototype.addInputParameter(OVP_Algorithm_DetectingMinMax_InputParameterId_SignalMatrix, "Signal input matrix", Kernel::ParameterType_Matrix);
|
||||
prototype.addInputParameter(OVP_Algorithm_DetectingMinMax_InputParameterId_TimeWindowStart, "Time window start", Kernel::ParameterType_Float);
|
||||
prototype.addInputParameter(OVP_Algorithm_DetectingMinMax_InputParameterId_TimeWindowEnd, "Time window end", Kernel::ParameterType_Float);
|
||||
prototype.addInputParameter(OVP_Algorithm_DetectingMinMax_InputParameterId_Sampling, "Sampling frequency", Kernel::ParameterType_UInteger);
|
||||
prototype.addOutputParameter(OVP_Algorithm_DetectingMinMax_OutputParameterId_SignalMatrix, "Signal output matrix", Kernel::ParameterType_Matrix);
|
||||
prototype.addInputTrigger(OVP_Algorithm_DetectingMinMax_InputTriggerId_Initialize, "Initialize");
|
||||
prototype.addInputTrigger(OVP_Algorithm_DetectingMinMax_InputTriggerId_DetectsMin, "Detects min");
|
||||
prototype.addInputTrigger(OVP_Algorithm_DetectingMinMax_InputTriggerId_DetectsMax, "Detects max");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IAlgorithmDesc, OVP_ClassId_Algorithm_DetectingMinMaxDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
#include "ovpCDownsampling.h"
|
||||
|
||||
#include <cmath> //floor, ceil
|
||||
#include <cstdlib>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
// ________________________________________________________________________________________________________________
|
||||
//
|
||||
|
||||
bool CDownsampling::initialize()
|
||||
{
|
||||
ip_sampling.initialize(getInputParameter(OVP_Algorithm_Downsampling_InputParameterId_Sampling));
|
||||
ip_newSampling.initialize(getInputParameter(OVP_Algorithm_Downsampling_InputParameterId_NewSampling));
|
||||
ip_signalMatrix.initialize(getInputParameter(OVP_Algorithm_Downsampling_InputParameterId_SignalMatrix));
|
||||
op_signalMatrix.initialize(getOutputParameter(OVP_Algorithm_Downsampling_OutputParameterId_SignalMatrix));
|
||||
|
||||
m_lastValueOrigSignal = nullptr;
|
||||
m_first = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDownsampling::uninitialize()
|
||||
{
|
||||
free(m_lastValueOrigSignal);
|
||||
m_lastValueOrigSignal = nullptr;
|
||||
|
||||
op_signalMatrix.uninitialize();
|
||||
ip_signalMatrix.uninitialize();
|
||||
ip_newSampling.uninitialize();
|
||||
ip_sampling.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ________________________________________________________________________________________________________________
|
||||
//
|
||||
|
||||
bool CDownsampling::process()
|
||||
{
|
||||
size_t oEpochSize, indexBegOutput;
|
||||
double blocDuration, endTime;
|
||||
|
||||
// signal input vars
|
||||
CMatrix* iMatrix = ip_signalMatrix;
|
||||
double* iBuffer = iMatrix->getBuffer();
|
||||
const size_t nChannels = ip_signalMatrix->getDimensionSize(0);
|
||||
const size_t iEpochSize = ip_signalMatrix->getDimensionSize(1);
|
||||
|
||||
const double iSampling = double(ip_sampling);
|
||||
const double oSampling = double(ip_newSampling);
|
||||
|
||||
// signal output vars
|
||||
CMatrix* oMatrix = op_signalMatrix;
|
||||
oMatrix->setDimensionCount(ip_signalMatrix->getDimensionCount());
|
||||
if ((m_first) || (isInputTriggerActive(OVP_Algorithm_Downsampling_InputTriggerId_Resample)))
|
||||
{
|
||||
oEpochSize = size_t(floor(iEpochSize * (oSampling / iSampling)));
|
||||
|
||||
blocDuration = double(iEpochSize - 1) / iSampling;
|
||||
endTime = blocDuration;
|
||||
|
||||
m_lastTimeOrigSignal = 0;
|
||||
m_lastTimeNewSignal = 0;
|
||||
|
||||
if (m_first)
|
||||
{
|
||||
m_lastValueOrigSignal = static_cast<double*>(calloc(nChannels, sizeof(double)));
|
||||
if (m_lastValueOrigSignal == nullptr) { this->getLogManager() << Kernel::LogLevel_Error << "Memory allocation : last values of original signal.\n"; }
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
blocDuration = double(iEpochSize) / iSampling;
|
||||
endTime = m_lastTimeOrigSignal + blocDuration;
|
||||
const double timePassed = endTime - m_lastTimeNewSignal;
|
||||
oEpochSize = size_t(floor(timePassed * oSampling));
|
||||
}
|
||||
|
||||
if (oEpochSize == 0)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Output epoch size is 0. Increase input epoch size.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// this->getLogManager() << Kernel::LogLevel_Info << "blockDur " << blocDuration << " et " << endTime << " lt " << m_lastTimeNewSignal << " td " << timeDiff << " dim " << oMatrixDimensionSizeEpoch << "\n";
|
||||
|
||||
oMatrix->setDimensionSize(0, nChannels);
|
||||
oMatrix->setDimensionSize(1, oEpochSize);
|
||||
double* oBuffer = oMatrix->getBuffer();
|
||||
|
||||
if (isInputTriggerActive(OVP_Algorithm_Downsampling_InputTriggerId_Initialize)) { }
|
||||
|
||||
if ((isInputTriggerActive(OVP_Algorithm_Downsampling_InputTriggerId_ResampleWithHistoric))
|
||||
|| (isInputTriggerActive(OVP_Algorithm_Downsampling_InputTriggerId_Resample)))
|
||||
{
|
||||
double countNew = 0, prev;
|
||||
int indexInput;
|
||||
|
||||
for (size_t i = 0; i < nChannels; ++i)
|
||||
{
|
||||
double countOrig = m_lastTimeOrigSignal;
|
||||
countNew = m_lastTimeNewSignal + (1.0 / double(ip_newSampling));
|
||||
double timePrev = m_lastTimeOrigSignal;
|
||||
if ((m_first) || (isInputTriggerActive(OVP_Algorithm_Downsampling_InputTriggerId_Resample)))
|
||||
{
|
||||
prev = iBuffer[i * iEpochSize];
|
||||
oBuffer[i * oEpochSize] = prev;
|
||||
indexBegOutput = 1;
|
||||
indexInput = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev = m_lastValueOrigSignal[i];
|
||||
indexBegOutput = 0;
|
||||
indexInput = -1;
|
||||
}
|
||||
for (uint64_t j = indexBegOutput; j < oEpochSize; ++j)
|
||||
{
|
||||
while ((indexInput < int(iEpochSize)) && (countOrig < countNew))
|
||||
{
|
||||
countOrig += 1.0 / double(ip_sampling);
|
||||
indexInput++;
|
||||
}
|
||||
|
||||
if (indexInput == -1) { this->getLogManager() << Kernel::LogLevel_Warning << "Downsampling problem : index value=-1\n"; }
|
||||
else if (indexInput < int(iEpochSize))
|
||||
{
|
||||
const double cur = iBuffer[(i * iEpochSize) + indexInput];
|
||||
oBuffer[(i * oEpochSize) + j] = ((cur - prev) * (countNew - timePrev) / (countOrig - timePrev)) + prev;
|
||||
prev = cur;
|
||||
timePrev = countOrig;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Warning << "Downsampling problem : sample #" << j << "/" << oEpochSize <<
|
||||
" time original signal=" << countOrig << " time new signal=" << countNew << " new signal sample #" << indexInput <<
|
||||
" /" << iEpochSize << "\n";
|
||||
j = oEpochSize;
|
||||
}
|
||||
|
||||
countNew += 1.0 / oSampling;
|
||||
}
|
||||
|
||||
if (oEpochSize > 0) { m_lastValueOrigSignal[i] = iBuffer[(i * iEpochSize) + iEpochSize - 1]; }
|
||||
}
|
||||
if (oEpochSize > 0)
|
||||
{
|
||||
m_lastTimeNewSignal = countNew - (1.0 / oSampling);
|
||||
m_lastTimeOrigSignal = endTime;
|
||||
}
|
||||
if (m_first) { m_first = false; }
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
class CDownsampling final : virtual public Toolkit::TAlgorithm<IAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TAlgorithm<IAlgorithm>, OVP_ClassId_Algorithm_Downsampling)
|
||||
|
||||
protected:
|
||||
|
||||
Kernel::TParameterHandler<uint64_t> ip_sampling;
|
||||
Kernel::TParameterHandler<uint64_t> ip_newSampling;
|
||||
Kernel::TParameterHandler<CMatrix*> ip_signalMatrix;
|
||||
Kernel::TParameterHandler<CMatrix*> op_signalMatrix;
|
||||
double* m_lastValueOrigSignal = nullptr;
|
||||
double m_lastTimeOrigSignal = 0;
|
||||
double m_lastTimeNewSignal = 0;
|
||||
bool m_first = false;
|
||||
};
|
||||
|
||||
class CDownsamplingDesc final : virtual public IAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Downsampling"); }
|
||||
CString getAuthorName() const override { return CString("G. Gibert - E. Maby - P.E. Aguera"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INSERM/U821"); }
|
||||
CString getShortDescription() const override { return CString("Downsamples input signal."); }
|
||||
|
||||
CString getDetailedDescription() const override { return CString("Downsamples input signal to the new sampling rate chosen by user."); }
|
||||
|
||||
CString getCategory() const override { return CString("Signal processing Gpl/Basic"); }
|
||||
CString getVersion() const override { return CString("1.1"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_Algorithm_Downsampling; }
|
||||
IPluginObject* create() override { return new CDownsampling(); }
|
||||
|
||||
bool getAlgorithmPrototype(Kernel::IAlgorithmProto& prototype) const override
|
||||
{
|
||||
prototype.addInputParameter(OVP_Algorithm_Downsampling_InputParameterId_Sampling, "Sampling frequency", Kernel::ParameterType_UInteger);
|
||||
prototype.addInputParameter(OVP_Algorithm_Downsampling_InputParameterId_NewSampling, "New sampling frequency", Kernel::ParameterType_UInteger);
|
||||
prototype.addInputParameter(OVP_Algorithm_Downsampling_InputParameterId_SignalMatrix, "Signal matrix", Kernel::ParameterType_Matrix);
|
||||
prototype.addOutputParameter(OVP_Algorithm_Downsampling_OutputParameterId_SignalMatrix, "Signal matrix", Kernel::ParameterType_Matrix);
|
||||
prototype.addInputTrigger(OVP_Algorithm_Downsampling_InputTriggerId_Initialize, "Initialize");
|
||||
prototype.addInputTrigger(OVP_Algorithm_Downsampling_InputTriggerId_Resample, "Resample");
|
||||
prototype.addInputTrigger(OVP_Algorithm_Downsampling_InputTriggerId_ResampleWithHistoric, "Resample with historic");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IAlgorithmDesc, OVP_ClassId_Algorithm_DownsamplingDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+307
@@ -0,0 +1,307 @@
|
||||
#if defined TARGET_HAS_ThirdPartyITPP
|
||||
|
||||
#include "ovpCBoxAlgorithmCSPSpatialFilterTrainer.h"
|
||||
|
||||
#include <complex>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <math.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <itpp/base/algebra/eigen.h>
|
||||
#include <itpp/base/algebra/inv.h>
|
||||
#include <itpp/stat/misc_stat.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
// Taken from http://techlogbook.wordpress.com/2009/08/12/adding-generalized-eigenvalue-functions-to-it
|
||||
// http://techlogbook.wordpress.com/2009/08/12/calling-lapack-functions-from-c-codes
|
||||
// http://sourceforge.net/projects/itpp/forums/forum/115656/topic/3363490?message=7557038
|
||||
//
|
||||
// http://icl.cs.utk.edu/projectsfiles/f2j/javadoc/org/netlib/lapack/DSYGV.html
|
||||
// http://www.lassp.cornell.edu/sethna/GeneDynamics/NetworkCodeDocumentation/lapack_8h.html#a17
|
||||
|
||||
namespace {
|
||||
extern "C" {
|
||||
// This symbol comes from LAPACK
|
||||
/*
|
||||
void zggev_(char *jobvl, char *jobvr, int *n, std::complex<double> *a,
|
||||
int *lda, std::complex<double> *b, int *ldb, std::complex<double> *alpha,
|
||||
std::complex<double> *beta, std::complex<double> *vl,
|
||||
int *ldvl, std::complex<double> *vr, int *ldvr,
|
||||
std::complex<double> *work, int *lwork, double *rwork, int *info);
|
||||
*/
|
||||
int dsygv_(int* itype, char* jobz, char* uplo, int* n, double* a,
|
||||
int* lda, double* b, int* ldb, double* w, double* work, int* lwork, int* info);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace itppextcsp {
|
||||
itpp::mat convert(const CMatrix& matrix)
|
||||
{
|
||||
itpp::mat res(matrix.getDimensionSize(1), matrix.getDimensionSize(0));
|
||||
if (matrix.getBufferElementCount() != 0) { memcpy(res._data(), matrix.getBuffer(), matrix.getBufferElementCount() * sizeof(double)); }
|
||||
return res.transpose();
|
||||
}
|
||||
|
||||
itpp::mat cov(const itpp::mat& matrix)
|
||||
{
|
||||
itpp::mat centered = repmat(sum(matrix, 2), 1, matrix.cols(), false);
|
||||
centered = centered / double(matrix.cols());
|
||||
centered = matrix - centered;
|
||||
itpp::mat res = centered * centered.transpose();
|
||||
res = res / double(matrix.cols() - 1);
|
||||
res = res / trace(res);
|
||||
return res;
|
||||
}
|
||||
} // namespace itppextcsp
|
||||
|
||||
bool CBoxAlgorithmCSPSpatialFilterTrainer::initialize()
|
||||
{
|
||||
m_stimDecoder = new Toolkit::TStimulationDecoder<CBoxAlgorithmCSPSpatialFilterTrainer>();
|
||||
m_stimDecoder->initialize(*this, 0);
|
||||
|
||||
m_signalDecoderCondition1 = new Toolkit::TSignalDecoder<CBoxAlgorithmCSPSpatialFilterTrainer>();
|
||||
m_signalDecoderCondition1->initialize(*this, 1);
|
||||
|
||||
m_signalDecoderCondition2 = new Toolkit::TSignalDecoder<CBoxAlgorithmCSPSpatialFilterTrainer>();
|
||||
m_signalDecoderCondition2->initialize(*this, 2);
|
||||
|
||||
m_encoder.initialize(*this, 0);
|
||||
|
||||
m_stimID = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
m_spatialFilterConfigFilename = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
|
||||
m_filterDimension = uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2));
|
||||
m_saveAsBoxConfig = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 3);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmCSPSpatialFilterTrainer::uninitialize()
|
||||
{
|
||||
m_signalDecoderCondition1->uninitialize();
|
||||
delete m_signalDecoderCondition1;
|
||||
m_signalDecoderCondition2->uninitialize();
|
||||
delete m_signalDecoderCondition2;
|
||||
m_stimDecoder->uninitialize();
|
||||
delete m_stimDecoder;
|
||||
|
||||
m_encoder.uninitialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmCSPSpatialFilterTrainer::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmCSPSpatialFilterTrainer::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
bool shouldTrain = false;
|
||||
uint64_t date = 0, startTime = 0, endTime = 0;
|
||||
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
m_stimDecoder->decode(i);
|
||||
if (m_stimDecoder->isHeaderReceived())
|
||||
{
|
||||
m_encoder.encodeHeader();
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
|
||||
}
|
||||
if (m_stimDecoder->isBufferReceived())
|
||||
{
|
||||
Kernel::TParameterHandler<IStimulationSet*> op_stimSet(m_stimDecoder->getOutputStimulationSet());
|
||||
for (size_t j = 0; j < op_stimSet->getStimulationCount(); ++j) { shouldTrain |= (op_stimSet->getStimulationIdentifier(j) == m_stimID); }
|
||||
if (shouldTrain)
|
||||
{
|
||||
date = op_stimSet->getStimulationDate(op_stimSet->getStimulationCount() - 1);
|
||||
startTime = boxContext.getInputChunkStartTime(0, i);
|
||||
endTime = boxContext.getInputChunkEndTime(0, i);
|
||||
}
|
||||
}
|
||||
if (m_stimDecoder->isEndReceived()) { m_encoder.encodeEnd(); }
|
||||
boxContext.markInputAsDeprecated(0, i);
|
||||
}
|
||||
|
||||
if (shouldTrain)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "Received train stimulation - be patient\n";
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Estimating cov for condition 1...\n";
|
||||
|
||||
itpp::mat covarianceMatrixCondition1;
|
||||
int nCondition1Trials = 0;
|
||||
int condition1ChunkSize = 0;
|
||||
int condition2ChunkSize = 0;
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(1); ++i)
|
||||
{
|
||||
m_signalDecoderCondition1->decode(i);
|
||||
if (m_signalDecoderCondition1->isHeaderReceived())
|
||||
{
|
||||
Kernel::TParameterHandler<CMatrix*> ip_matrix(m_signalDecoderCondition1->getOutputMatrix());
|
||||
covarianceMatrixCondition1.set_size(ip_matrix->getDimensionSize(0), ip_matrix->getDimensionSize(0));
|
||||
covarianceMatrixCondition1.zeros();
|
||||
|
||||
condition1ChunkSize = ip_matrix->getDimensionSize(1);
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << "Cov matrix size for condition 1 is [" << ip_matrix->getDimensionSize(0) << "x"
|
||||
<< ip_matrix->getDimensionSize(0) << "], chunk size is " << condition1ChunkSize << " samples\n";
|
||||
}
|
||||
if (m_signalDecoderCondition1->isBufferReceived())
|
||||
{
|
||||
Kernel::TParameterHandler<CMatrix*> ip_matrix(m_signalDecoderCondition1->getOutputMatrix());
|
||||
itpp::mat matrix = itppextcsp::convert(*ip_matrix);
|
||||
covarianceMatrixCondition1 += itppextcsp::cov(matrix);
|
||||
nCondition1Trials++;
|
||||
}
|
||||
if (m_signalDecoderCondition1->isEndReceived()) { }
|
||||
boxContext.markInputAsDeprecated(1, i);
|
||||
}
|
||||
covarianceMatrixCondition1 = covarianceMatrixCondition1 / double(nCondition1Trials);
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Number of chunks for condition 1: " << nCondition1Trials << "\n";
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Estimating cov for condition 2...\n";
|
||||
|
||||
itpp::mat covarianceMatrixCondition2;
|
||||
int nCondition2Trials = 0;
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(2); ++i)
|
||||
{
|
||||
m_signalDecoderCondition2->decode(i);
|
||||
if (m_signalDecoderCondition2->isHeaderReceived())
|
||||
{
|
||||
Kernel::TParameterHandler<CMatrix*> ip_matrix(m_signalDecoderCondition2->getOutputMatrix());
|
||||
covarianceMatrixCondition2.set_size(ip_matrix->getDimensionSize(0), ip_matrix->getDimensionSize(0));
|
||||
covarianceMatrixCondition2.zeros();
|
||||
|
||||
condition2ChunkSize = ip_matrix->getDimensionSize(1);
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << "Cov matrix size for condition 2 is [" << ip_matrix->getDimensionSize(0) << "x"
|
||||
<< ip_matrix->getDimensionSize(0) << "], chunk size is " << condition2ChunkSize << " samples\n";
|
||||
}
|
||||
if (m_signalDecoderCondition2->isBufferReceived())
|
||||
{
|
||||
Kernel::TParameterHandler<CMatrix*> ip_matrix(m_signalDecoderCondition2->getOutputMatrix());
|
||||
itpp::mat matrix = itppextcsp::convert(*ip_matrix);
|
||||
covarianceMatrixCondition2 += itppextcsp::cov(matrix);
|
||||
nCondition2Trials++;
|
||||
}
|
||||
if (m_signalDecoderCondition2->isEndReceived()) { }
|
||||
boxContext.markInputAsDeprecated(2, i);
|
||||
}
|
||||
covarianceMatrixCondition2 = covarianceMatrixCondition2 / double(nCondition2Trials);
|
||||
|
||||
if (covarianceMatrixCondition1.cols() != covarianceMatrixCondition2.cols())
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "The two inputs do not seem to have the same number of channels, "
|
||||
<< covarianceMatrixCondition1.cols() << " vs " << covarianceMatrixCondition2.cols() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "Data covariance dims are [" << covarianceMatrixCondition1.rows() << "x" << covarianceMatrixCondition1.cols()
|
||||
<< "]. Number of samples per condition : \n";
|
||||
this->getLogManager() << Kernel::LogLevel_Info << " cond1 = " << nCondition1Trials << " chunks, sized " << condition1ChunkSize << " -> "
|
||||
<< nCondition1Trials * condition1ChunkSize << " samples\n";
|
||||
this->getLogManager() << Kernel::LogLevel_Info << " cond2 = " << nCondition2Trials << " chunks, sized " << condition2ChunkSize << " -> "
|
||||
<< nCondition2Trials * condition2ChunkSize << " samples\n";
|
||||
|
||||
if (nCondition1Trials == 0 || nCondition2Trials == 0)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "No signal received... Can't continue\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Trace << "Computing eigen vector decomposition...\n";
|
||||
|
||||
itpp::cmat eigenVector;
|
||||
itpp::cvec eigenValue;
|
||||
size_t nChannel = covarianceMatrixCondition1.rows();
|
||||
|
||||
if (eig(inv(covarianceMatrixCondition2) * covarianceMatrixCondition1, eigenValue, eigenVector))
|
||||
{
|
||||
std::map<double, itpp::vec> vEigenVector;
|
||||
for (size_t i = 0; i < nChannel; ++i)
|
||||
{
|
||||
itpp::cvec v = eigenVector.get_col(i);
|
||||
vEigenVector[itpp::real(eigenValue)[i]] = itpp::real(v);
|
||||
}
|
||||
|
||||
// Collect the output vectors here
|
||||
CMatrix outputVectors(m_filterDimension, nChannel);
|
||||
|
||||
size_t steps = 0, cnt = 0;
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << "lowest eigenvalues: " << "\n";
|
||||
for (auto it = vEigenVector.begin(); it != vEigenVector.end() && steps < ceil(m_filterDimension / 2.0); ++it, steps++)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << it->first << ", ";
|
||||
for (size_t j = 0; j < nChannel; ++j) { outputVectors.getBuffer()[cnt++] = it->second[j]; }
|
||||
}
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << "\n";
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << "highest eigenvalues: " << "\n";
|
||||
steps = 0;
|
||||
for (auto it = vEigenVector.rbegin(); it != vEigenVector.rend() && steps < floor(m_filterDimension / 2.0); ++it, steps++)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << it->first << ", ";
|
||||
for (size_t j = 0; j < nChannel; ++j) { outputVectors.getBuffer()[cnt++] = it->second[j]; }
|
||||
}
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << "\n";
|
||||
|
||||
if (m_saveAsBoxConfig)
|
||||
{
|
||||
FILE* file = fopen(m_spatialFilterConfigFilename.toASCIIString(), "wb");
|
||||
if (!file)
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "The file [" << m_spatialFilterConfigFilename <<
|
||||
"] could not be opened for writing...\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(file, "<OpenViBE-SettingsOverride>\n");
|
||||
fprintf(file, "\t<SettingValue>");
|
||||
|
||||
cnt = 0;
|
||||
for (size_t i = 0; i < m_filterDimension; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < nChannel; ++j) { fprintf(file, "%e ", outputVectors.getBuffer()[cnt++]); }
|
||||
}
|
||||
|
||||
fprintf(file, "</SettingValue>\n");
|
||||
fprintf(file, "\t<SettingValue>%d</SettingValue>\n", m_filterDimension);
|
||||
fprintf(file, "\t<SettingValue>%d</SettingValue>\n", nChannel);
|
||||
fprintf(file, "\t<SettingValue></SettingValue>\n");
|
||||
fprintf(file, "</OpenViBE-SettingsOverride>\n");
|
||||
fclose(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Toolkit::Matrix::saveToTextFile(outputVectors, m_spatialFilterConfigFilename))
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Unable to save to [" << m_spatialFilterConfigFilename << "\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_ImportantWarning << "Eigen vector decomposition failed...\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
this->getLogManager() << Kernel::LogLevel_Info << "CSP Spatial filter trained successfully.\n";
|
||||
|
||||
m_encoder.getInputStimulationSet()->clear();
|
||||
m_encoder.getInputStimulationSet()->appendStimulation(OVTK_StimulationId_TrainCompleted, date, 0);
|
||||
m_encoder.encodeBuffer();
|
||||
|
||||
boxContext.markOutputAsReadyToSend(0, startTime, endTime);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
#endif // TARGET_HAS_ThirdPartyITPP
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
// @copyright notice: Possibly due to dependencies, this box used to be GPL before upgrade to AGPL3
|
||||
|
||||
#pragma once
|
||||
#include "../ovp_defines.h"
|
||||
|
||||
#if defined TARGET_HAS_ThirdPartyITPP
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
class CBoxAlgorithmCSPSpatialFilterTrainer final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_CSPSpatialFilterTrainer)
|
||||
|
||||
protected:
|
||||
|
||||
Toolkit::TStimulationDecoder<CBoxAlgorithmCSPSpatialFilterTrainer>* m_stimDecoder = nullptr;
|
||||
Toolkit::TSignalDecoder<CBoxAlgorithmCSPSpatialFilterTrainer>* m_signalDecoderCondition1 = nullptr;
|
||||
Toolkit::TSignalDecoder<CBoxAlgorithmCSPSpatialFilterTrainer>* m_signalDecoderCondition2 = nullptr;
|
||||
|
||||
|
||||
Toolkit::TStimulationEncoder<CBoxAlgorithmCSPSpatialFilterTrainer> m_encoder;
|
||||
|
||||
uint64_t m_stimID = 0;
|
||||
CString m_spatialFilterConfigFilename;
|
||||
size_t m_filterDimension = 0;
|
||||
bool m_saveAsBoxConfig = false;
|
||||
};
|
||||
|
||||
class CBoxAlgorithmCSPSpatialFilterTrainerDesc final : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
CString getName() const override { return CString("CSP Spatial Filter Trainer"); }
|
||||
CString getAuthorName() const override { return CString("Dieter Devlaminck"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Ghent University"); }
|
||||
|
||||
CString getShortDescription() const override { return CString("Computes spatial filter coeffcients according to the Common Spatial Pattern algorithm."); }
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return CString(
|
||||
"The CSP algortihm increases the signal variance for one condition while minimizing the variance for the other condition.");
|
||||
}
|
||||
|
||||
CString getCategory() const override { return CString("Signal processing/Filtering"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getStockItemName() const override { return CString(""); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_CSPSpatialFilterTrainer; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmCSPSpatialFilterTrainer; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Stimulations", OV_TypeId_Stimulations);
|
||||
prototype.addInput("Signal condition 1", OV_TypeId_Signal);
|
||||
prototype.addInput("Signal condition 2", OV_TypeId_Signal);
|
||||
prototype.addSetting("Train Trigger", OV_TypeId_Stimulation, "OVTK_GDF_End_Of_Session");
|
||||
prototype.addSetting("Spatial filter configuration", OV_TypeId_Filename, "");
|
||||
prototype.addSetting("Filter dimension", OV_TypeId_Integer, "2");
|
||||
prototype.addSetting("Save as box config", OV_TypeId_Boolean, "true");
|
||||
|
||||
prototype.addOutput("Train-completed Flag", OV_TypeId_Stimulations);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_CSPSpatialFilterTrainerDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
|
||||
#endif // TARGET_HAS_ThirdPartyITPP
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
#include "ovpCBoxAlgorithmSynchro.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CBoxAlgorithmSynchro::initialize()
|
||||
{
|
||||
m_inputChannel.initialize(this);
|
||||
m_outputChannel.initialize(this);
|
||||
m_stimulationReceivedStart = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmSynchro::uninitialize()
|
||||
{
|
||||
m_inputChannel.uninitialize();
|
||||
m_outputChannel.uninitialize();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmSynchro::processInput(const size_t /*index*/)
|
||||
{
|
||||
this->getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmSynchro::process()
|
||||
{
|
||||
// FIXME is it necessary to keep next line uncomment ?
|
||||
//IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
if (m_inputChannel.isWorking())
|
||||
{
|
||||
// process stimulations
|
||||
for (size_t index = 0, nb = m_inputChannel.getNStimulationBuffers(); index < nb; ++index)
|
||||
{
|
||||
uint64_t startTime, endTime;
|
||||
IStimulationSet* stimset = m_inputChannel.getStimulation(startTime, endTime, index);
|
||||
|
||||
if (!stimset) { break; }
|
||||
|
||||
m_outputChannel.sendStimulation(stimset, startTime, endTime);
|
||||
}
|
||||
// process signal
|
||||
for (size_t index = 0, nb = m_inputChannel.getNSignalBuffers(); index < nb; ++index)
|
||||
{
|
||||
uint64_t startTime, endTime;
|
||||
CMatrix* matrix = m_inputChannel.getSignal(startTime, endTime, index++);
|
||||
|
||||
if (!matrix) { break; }
|
||||
|
||||
m_outputChannel.sendSignal(matrix, startTime, endTime);
|
||||
}
|
||||
}
|
||||
else if (m_inputChannel.hasSynchro())
|
||||
{
|
||||
m_outputChannel.processSynchroSignal(m_inputChannel.getStimulationPosition(), m_inputChannel.getSignalPosition());
|
||||
m_inputChannel.startWorking();
|
||||
}
|
||||
else if (m_inputChannel.hasHeader()) { m_inputChannel.waitForSynchro(); }
|
||||
else if (m_inputChannel.waitForSignalHeader()) { m_outputChannel.sendHeader(m_inputChannel.getSamplingRate(), m_inputChannel.getMatrixPtr()); }
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "ovpCInputChannel.h"
|
||||
#include "ovpCOutputChannel.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
class CBoxAlgorithmSynchro final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_Synchro)
|
||||
|
||||
protected:
|
||||
|
||||
//Intern ressources
|
||||
bool m_stimulationReceivedStart = false;
|
||||
|
||||
// new
|
||||
CInputChannel m_inputChannel;
|
||||
COutputChannel m_outputChannel;
|
||||
};
|
||||
|
||||
class CBoxAlgorithmSynchroDesc final : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Stream Synchronization"); }
|
||||
CString getAuthorName() const override { return CString("Gelu Ionescu & Matthieu Goyat"); }
|
||||
CString getAuthorCompanyName() const override { return CString("GIPSA-lab"); }
|
||||
CString getShortDescription() const override { return CString("Synchronize two acquisition servers"); }
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString("Signal processing/Basic"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getStockItemName() const override { return CString("gtk-missing-image"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_Synchro; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmSynchro; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Input signal", OV_TypeId_Signal);
|
||||
prototype.addInput("Input stimulation", OV_TypeId_Stimulations);
|
||||
prototype.addOutput("Output signal", OV_TypeId_Signal);
|
||||
prototype.addOutput("Output stimulation", OV_TypeId_Stimulations);
|
||||
prototype.addSetting("Synchronisation stimulation", OV_TypeId_Stimulation, "OVTK_StimulationId_ExperimentStart");
|
||||
// prototype.addFlag (Kernel::BoxFlag_CanModifyInput);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_SynchroDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+360
@@ -0,0 +1,360 @@
|
||||
#include "ovpCBoxAlgorithmUnivariateStatistics.h"
|
||||
#include "../algorithms/ovpCAlgorithmUnivariateStatistics.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
|
||||
bool CBoxUnivariateStatistic::initialize()
|
||||
{
|
||||
//initialise en/decoder function of the input type
|
||||
this->getStaticBoxContext().getInputType(0, m_inputTypeID);
|
||||
|
||||
#if 0 // this is not needed as you know you always habe signal
|
||||
if(m_inputTypeID==OV_TypeId_StreamedMatrix)
|
||||
{
|
||||
m_decoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StreamedMatrixDecoder));
|
||||
m_meanEncoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StreamedMatrixEncoder));
|
||||
m_varianceEncoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StreamedMatrixEncoder));
|
||||
m_rangeEncoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StreamedMatrixEncoder));
|
||||
m_medianEncoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StreamedMatrixEncoder));
|
||||
m_iqrEncoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StreamedMatrixEncoder));
|
||||
m_percentileEncoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StreamedMatrixEncoder));
|
||||
}
|
||||
else if(m_inputTypeID==OV_TypeId_FeatureVector)
|
||||
{
|
||||
m_decoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_FeatureVectorDecoder));
|
||||
m_meanEncoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_FeatureVectorEncoder));
|
||||
m_varianceEncoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_FeatureVectorEncoder));
|
||||
m_rangeEncoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_FeatureVectorEncoder));
|
||||
m_medianEncoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_FeatureVectorEncoder));
|
||||
m_iqrEncoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_FeatureVectorEncoder));
|
||||
m_percentileEncoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_FeatureVectorEncoder));
|
||||
}
|
||||
else if(m_inputTypeID==OV_TypeId_Signal)
|
||||
{
|
||||
#endif
|
||||
m_decoder = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SignalDecoder));
|
||||
m_meanEncoder = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SignalEncoder));
|
||||
m_varianceEncoder = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SignalEncoder));
|
||||
m_rangeEncoder = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SignalEncoder));
|
||||
m_medianEncoder = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SignalEncoder));
|
||||
m_iqrEncoder = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SignalEncoder));
|
||||
m_percentileEncoder = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SignalEncoder));
|
||||
#if 0 // this is not needed as you know you always habe signal
|
||||
}
|
||||
else if(m_inputTypeID==OV_TypeId_Spectrum)
|
||||
{
|
||||
m_decoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SpectrumDecoder));
|
||||
m_meanEncoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SpectrumEncoder));
|
||||
m_varianceEncoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SpectrumEncoder));
|
||||
m_rangeEncoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SpectrumEncoder));
|
||||
m_medianEncoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SpectrumEncoder));
|
||||
m_iqrEncoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SpectrumEncoder));
|
||||
m_percentileEncoder=&getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_SpectrumEncoder));
|
||||
}
|
||||
else
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << "Input type is not planned : no matrix base. This box can't work, so it is disabled\n";
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_decoder->initialize();
|
||||
m_meanEncoder->initialize();
|
||||
m_varianceEncoder->initialize();
|
||||
m_rangeEncoder->initialize();
|
||||
m_medianEncoder->initialize();
|
||||
m_iqrEncoder->initialize();
|
||||
m_percentileEncoder->initialize();
|
||||
|
||||
//initialize the real algorithm this box encapsulate
|
||||
m_matrixStatistic = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_ClassId_AlgoUnivariateStatistic));
|
||||
m_matrixStatistic->initialize();
|
||||
|
||||
//initialize all handlers
|
||||
m_matrixStatistic->getInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_Matrix)->setReferenceTarget(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputParameterId_Matrix));
|
||||
m_meanEncoder->getInputParameter(OVP_GD_Algorithm_StreamedMatrixEncoder_InputParameterId_Matrix)->setReferenceTarget(
|
||||
m_matrixStatistic->getOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_Mean));
|
||||
m_varianceEncoder->getInputParameter(OVP_GD_Algorithm_StreamedMatrixEncoder_InputParameterId_Matrix)->setReferenceTarget(
|
||||
m_matrixStatistic->getOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_Var));
|
||||
m_rangeEncoder->getInputParameter(OVP_GD_Algorithm_StreamedMatrixEncoder_InputParameterId_Matrix)->setReferenceTarget(
|
||||
m_matrixStatistic->getOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_Range));
|
||||
m_medianEncoder->getInputParameter(OVP_GD_Algorithm_StreamedMatrixEncoder_InputParameterId_Matrix)->setReferenceTarget(
|
||||
m_matrixStatistic->getOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_Med));
|
||||
m_iqrEncoder->getInputParameter(OVP_GD_Algorithm_StreamedMatrixEncoder_InputParameterId_Matrix)->setReferenceTarget(
|
||||
m_matrixStatistic->getOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_IQR));
|
||||
m_percentileEncoder->getInputParameter(OVP_GD_Algorithm_StreamedMatrixEncoder_InputParameterId_Matrix)->setReferenceTarget(
|
||||
m_matrixStatistic->getOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_Percent));
|
||||
|
||||
#if 0 // this is not needed as you know you always habe signal
|
||||
|
||||
/// specific connection for what is different of matrix base
|
||||
if(m_inputTypeID==OV_TypeId_Signal)
|
||||
{
|
||||
#endif
|
||||
op_sampling.initialize(m_decoder->getOutputParameter(OVP_GD_Algorithm_SignalDecoder_OutputParameterId_Sampling));
|
||||
m_meanEncoder->getInputParameter(OVP_GD_Algorithm_SignalEncoder_InputParameterId_Sampling)->setReferenceTarget(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_SignalDecoder_OutputParameterId_Sampling));
|
||||
m_varianceEncoder->getInputParameter(OVP_GD_Algorithm_SignalEncoder_InputParameterId_Sampling)->setReferenceTarget(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_SignalDecoder_OutputParameterId_Sampling));
|
||||
m_rangeEncoder->getInputParameter(OVP_GD_Algorithm_SignalEncoder_InputParameterId_Sampling)->setReferenceTarget(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_SignalDecoder_OutputParameterId_Sampling));
|
||||
m_medianEncoder->getInputParameter(OVP_GD_Algorithm_SignalEncoder_InputParameterId_Sampling)->setReferenceTarget(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_SignalDecoder_OutputParameterId_Sampling));
|
||||
m_iqrEncoder->getInputParameter(OVP_GD_Algorithm_SignalEncoder_InputParameterId_Sampling)->setReferenceTarget(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_SignalDecoder_OutputParameterId_Sampling));
|
||||
m_percentileEncoder->getInputParameter(OVP_GD_Algorithm_SignalEncoder_InputParameterId_Sampling)->setReferenceTarget(
|
||||
m_decoder->getOutputParameter(OVP_GD_Algorithm_SignalDecoder_OutputParameterId_Sampling));
|
||||
#if 0 // this is not needed as you know you always habe signal
|
||||
}
|
||||
else if(m_inputTypeID==OV_TypeId_Spectrum)
|
||||
{
|
||||
m_meanEncoder->getInputParameter(OVP_GD_Algorithm_SpectrumEncoder_InputParameterId_MinMaxFrequencyBands)->setReferenceTarget(m_decoder->getOutputParameter(OVP_GD_Algorithm_SpectrumDecoder_OutputParameterId_MinMaxFrequencyBands));
|
||||
m_varianceEncoder->getInputParameter(OVP_GD_Algorithm_SpectrumEncoder_InputParameterId_MinMaxFrequencyBands)->setReferenceTarget(m_decoder->getOutputParameter(OVP_GD_Algorithm_SpectrumDecoder_OutputParameterId_MinMaxFrequencyBands));
|
||||
m_rangeEncoder->getInputParameter(OVP_GD_Algorithm_SpectrumEncoder_InputParameterId_MinMaxFrequencyBands)->setReferenceTarget(m_decoder->getOutputParameter(OVP_GD_Algorithm_SpectrumDecoder_OutputParameterId_MinMaxFrequencyBands));
|
||||
m_medianEncoder->getInputParameter(OVP_GD_Algorithm_SpectrumEncoder_InputParameterId_MinMaxFrequencyBands)->setReferenceTarget(m_decoder->getOutputParameter(OVP_GD_Algorithm_SpectrumDecoder_OutputParameterId_MinMaxFrequencyBands));
|
||||
m_iqrEncoder->getInputParameter(OVP_GD_Algorithm_SpectrumEncoder_InputParameterId_MinMaxFrequencyBands)->setReferenceTarget(m_decoder->getOutputParameter(OVP_GD_Algorithm_SpectrumDecoder_OutputParameterId_MinMaxFrequencyBands));
|
||||
m_percentileEncoder->getInputParameter(OVP_GD_Algorithm_SpectrumEncoder_InputParameterId_MinMaxFrequencyBands)->setReferenceTarget(m_decoder->getOutputParameter(OVP_GD_Algorithm_SpectrumDecoder_OutputParameterId_MinMaxFrequencyBands));
|
||||
}
|
||||
#endif
|
||||
|
||||
op_compression.initialize(m_matrixStatistic->getOutputParameter(OVP_Algorithm_UnivariateStatistic_OutputParameterId_Compression));
|
||||
|
||||
ip_isMeanActive.initialize(m_matrixStatistic->getInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_MeanActive));
|
||||
ip_isVarianceActive.initialize(m_matrixStatistic->getInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_VarActive));
|
||||
ip_isRangeActive.initialize(m_matrixStatistic->getInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_RangeActive));
|
||||
ip_isMedianActive.initialize(m_matrixStatistic->getInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_MedActive));
|
||||
ip_isIQRActive.initialize(m_matrixStatistic->getInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_IQRActive));
|
||||
ip_isPercentileActive.initialize(m_matrixStatistic->getInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_PercentActive));
|
||||
|
||||
//get dis/enabled output wanted
|
||||
ip_isMeanActive = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
|
||||
ip_isVarianceActive = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
|
||||
ip_isRangeActive = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2);
|
||||
ip_isMedianActive = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 3);
|
||||
ip_isIQRActive = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 4);
|
||||
ip_isPercentileActive = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 5);
|
||||
|
||||
//the percentile value
|
||||
ip_parameterValue.initialize(m_matrixStatistic->getInputParameter(OVP_Algorithm_UnivariateStatistic_InputParameterId_PercentValue));
|
||||
ip_parameterValue = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 6);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxUnivariateStatistic::uninitialize()
|
||||
{
|
||||
#if 0
|
||||
if(m_inputTypeID==OV_TypeId_Signal)
|
||||
{
|
||||
#endif
|
||||
op_sampling.uninitialize();
|
||||
#if 0
|
||||
}
|
||||
#endif
|
||||
|
||||
ip_parameterValue.uninitialize();
|
||||
|
||||
m_matrixStatistic->uninitialize();
|
||||
m_meanEncoder->uninitialize();
|
||||
m_varianceEncoder->uninitialize();
|
||||
m_rangeEncoder->uninitialize();
|
||||
m_medianEncoder->uninitialize();
|
||||
m_iqrEncoder->uninitialize();
|
||||
m_percentileEncoder->uninitialize();
|
||||
m_decoder->uninitialize();
|
||||
|
||||
this->getAlgorithmManager().releaseAlgorithm(*m_matrixStatistic);
|
||||
this->getAlgorithmManager().releaseAlgorithm(*m_meanEncoder);
|
||||
this->getAlgorithmManager().releaseAlgorithm(*m_varianceEncoder);
|
||||
this->getAlgorithmManager().releaseAlgorithm(*m_rangeEncoder);
|
||||
this->getAlgorithmManager().releaseAlgorithm(*m_medianEncoder);
|
||||
this->getAlgorithmManager().releaseAlgorithm(*m_iqrEncoder);
|
||||
this->getAlgorithmManager().releaseAlgorithm(*m_percentileEncoder);
|
||||
this->getAlgorithmManager().releaseAlgorithm(*m_decoder);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxUnivariateStatistic::processInput(const size_t /*index*/)
|
||||
{
|
||||
this->getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxUnivariateStatistic::process()
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = getDynamicBoxContext();
|
||||
|
||||
//for each input, calculate statistics and return the value
|
||||
for (size_t j = 0; j < boxContext.getInputChunkCount(0); ++j)
|
||||
{
|
||||
Kernel::TParameterHandler<const IMemoryBuffer*> iBufferHandle(
|
||||
m_decoder->getInputParameter(OVP_GD_Algorithm_StreamedMatrixDecoder_InputParameterId_MemoryBufferToDecode));
|
||||
Kernel::TParameterHandler<IMemoryBuffer*> oBufferHandleMean(
|
||||
m_meanEncoder->getOutputParameter(OVP_GD_Algorithm_StreamedMatrixEncoder_OutputParameterId_EncodedMemoryBuffer));
|
||||
Kernel::TParameterHandler<IMemoryBuffer*> oBufferHandleVar(
|
||||
m_varianceEncoder->getOutputParameter(OVP_GD_Algorithm_StreamedMatrixEncoder_OutputParameterId_EncodedMemoryBuffer));
|
||||
Kernel::TParameterHandler<IMemoryBuffer*> oBufferHandleRange(
|
||||
m_rangeEncoder->getOutputParameter(OVP_GD_Algorithm_StreamedMatrixEncoder_OutputParameterId_EncodedMemoryBuffer));
|
||||
Kernel::TParameterHandler<IMemoryBuffer*> oBufferHandleMedian(
|
||||
m_medianEncoder->getOutputParameter(OVP_GD_Algorithm_StreamedMatrixEncoder_OutputParameterId_EncodedMemoryBuffer));
|
||||
Kernel::TParameterHandler<IMemoryBuffer*> oBufferHandleIqr(
|
||||
m_iqrEncoder->getOutputParameter(OVP_GD_Algorithm_StreamedMatrixEncoder_OutputParameterId_EncodedMemoryBuffer));
|
||||
Kernel::TParameterHandler<IMemoryBuffer*> oBufferHandlePercent(
|
||||
m_percentileEncoder->getOutputParameter(OVP_GD_Algorithm_StreamedMatrixEncoder_OutputParameterId_EncodedMemoryBuffer));
|
||||
iBufferHandle = boxContext.getInputChunk(0, j);
|
||||
oBufferHandleMean = boxContext.getOutputChunk(0);
|
||||
oBufferHandleVar = boxContext.getOutputChunk(1);
|
||||
oBufferHandleRange = boxContext.getOutputChunk(2);
|
||||
oBufferHandleMedian = boxContext.getOutputChunk(3);
|
||||
oBufferHandleIqr = boxContext.getOutputChunk(4);
|
||||
oBufferHandlePercent = boxContext.getOutputChunk(5);
|
||||
|
||||
m_decoder->process();
|
||||
|
||||
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputTriggerId_ReceivedHeader))
|
||||
{
|
||||
m_matrixStatistic->process(OVP_Algorithm_UnivariateStatistic_InputTriggerId_Initialize);
|
||||
|
||||
#if 0 // this is not needed as you know you always habe signal
|
||||
if(m_inputTypeID==OV_TypeId_FeatureVector)
|
||||
{
|
||||
}
|
||||
if(m_inputTypeID==OV_TypeId_Signal)
|
||||
{
|
||||
#endif
|
||||
this->getLogManager() << Kernel::LogLevel_Debug << "DownSampling information : " << op_sampling << "*" << op_compression << "=>" <<
|
||||
op_sampling * op_compression << "\n";
|
||||
op_sampling = uint64_t(op_sampling * op_compression);
|
||||
if (op_sampling == 0) { this->getLogManager() << Kernel::LogLevel_Warning << "Output sampling Rate is null, it could produce problem in next boxes \n"; }
|
||||
#if 0 // this is not needed as you know you always habe signal
|
||||
}
|
||||
else if(m_inputTypeID==OV_TypeId_Spectrum)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ip_isMeanActive)
|
||||
{
|
||||
m_meanEncoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeHeader);
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, j), boxContext.getInputChunkEndTime(0, j));
|
||||
}
|
||||
if (ip_isVarianceActive)
|
||||
{
|
||||
m_varianceEncoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeHeader);
|
||||
boxContext.markOutputAsReadyToSend(1, boxContext.getInputChunkStartTime(0, j), boxContext.getInputChunkEndTime(0, j));
|
||||
}
|
||||
if (ip_isRangeActive)
|
||||
{
|
||||
m_rangeEncoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeHeader);
|
||||
boxContext.markOutputAsReadyToSend(2, boxContext.getInputChunkStartTime(0, j), boxContext.getInputChunkEndTime(0, j));
|
||||
}
|
||||
if (ip_isMedianActive)
|
||||
{
|
||||
m_medianEncoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeHeader);
|
||||
boxContext.markOutputAsReadyToSend(3, boxContext.getInputChunkStartTime(0, j), boxContext.getInputChunkEndTime(0, j));
|
||||
}
|
||||
if (ip_isIQRActive)
|
||||
{
|
||||
m_iqrEncoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeHeader);
|
||||
boxContext.markOutputAsReadyToSend(4, boxContext.getInputChunkStartTime(0, j), boxContext.getInputChunkEndTime(0, j));
|
||||
}
|
||||
if (ip_isPercentileActive)
|
||||
{
|
||||
m_percentileEncoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeHeader);
|
||||
boxContext.markOutputAsReadyToSend(5, boxContext.getInputChunkStartTime(0, j), boxContext.getInputChunkEndTime(0, j));
|
||||
}
|
||||
}//end header
|
||||
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputTriggerId_ReceivedBuffer))
|
||||
{
|
||||
m_matrixStatistic->process(OVP_Algorithm_UnivariateStatistic_InputTriggerId_Process);
|
||||
if (m_matrixStatistic->isOutputTriggerActive(OVP_Algorithm_UnivariateStatistic_OutputTriggerId_ProcessDone))
|
||||
{
|
||||
#if 0
|
||||
if(m_inputTypeID == OV_TypeId_FeatureVector) { }
|
||||
if(m_inputTypeID == OV_TypeId_Signal) { }
|
||||
else if(m_inputTypeID == OV_TypeId_Spectrum) { }
|
||||
#endif
|
||||
if (ip_isMeanActive)
|
||||
{
|
||||
m_meanEncoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeBuffer);
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, j), boxContext.getInputChunkEndTime(0, j));
|
||||
}
|
||||
if (ip_isVarianceActive)
|
||||
{
|
||||
m_varianceEncoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeBuffer);
|
||||
boxContext.markOutputAsReadyToSend(1, boxContext.getInputChunkStartTime(0, j), boxContext.getInputChunkEndTime(0, j));
|
||||
}
|
||||
if (ip_isRangeActive)
|
||||
{
|
||||
m_rangeEncoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeBuffer);
|
||||
boxContext.markOutputAsReadyToSend(2, boxContext.getInputChunkStartTime(0, j), boxContext.getInputChunkEndTime(0, j));
|
||||
}
|
||||
if (ip_isMedianActive)
|
||||
{
|
||||
m_medianEncoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeBuffer);
|
||||
boxContext.markOutputAsReadyToSend(3, boxContext.getInputChunkStartTime(0, j), boxContext.getInputChunkEndTime(0, j));
|
||||
}
|
||||
if (ip_isIQRActive)
|
||||
{
|
||||
m_iqrEncoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeBuffer);
|
||||
boxContext.markOutputAsReadyToSend(4, boxContext.getInputChunkStartTime(0, j), boxContext.getInputChunkEndTime(0, j));
|
||||
}
|
||||
if (ip_isPercentileActive)
|
||||
{
|
||||
m_percentileEncoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeBuffer);
|
||||
boxContext.markOutputAsReadyToSend(5, boxContext.getInputChunkStartTime(0, j), boxContext.getInputChunkEndTime(0, j));
|
||||
}
|
||||
}
|
||||
else { this->getLogManager() << Kernel::LogLevel_Debug << "Process not activated\n"; }
|
||||
}//end buffer
|
||||
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputTriggerId_ReceivedEnd))
|
||||
{
|
||||
#if 0
|
||||
if(m_inputTypeID == OV_TypeId_FeatureVector) { }
|
||||
if(m_inputTypeID == OV_TypeId_Signal) { }
|
||||
else if(m_inputTypeID == OV_TypeId_Spectrum) { }
|
||||
#endif
|
||||
if (ip_isMeanActive)
|
||||
{
|
||||
m_meanEncoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeEnd);
|
||||
boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, j), boxContext.getInputChunkEndTime(0, j));
|
||||
}
|
||||
if (ip_isVarianceActive)
|
||||
{
|
||||
m_varianceEncoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeEnd);
|
||||
boxContext.markOutputAsReadyToSend(1, boxContext.getInputChunkStartTime(0, j), boxContext.getInputChunkEndTime(0, j));
|
||||
}
|
||||
if (ip_isRangeActive)
|
||||
{
|
||||
m_rangeEncoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeEnd);
|
||||
boxContext.markOutputAsReadyToSend(2, boxContext.getInputChunkStartTime(0, j), boxContext.getInputChunkEndTime(0, j));
|
||||
}
|
||||
if (ip_isMedianActive)
|
||||
{
|
||||
m_medianEncoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeEnd);
|
||||
boxContext.markOutputAsReadyToSend(3, boxContext.getInputChunkStartTime(0, j), boxContext.getInputChunkEndTime(0, j));
|
||||
}
|
||||
if (ip_isIQRActive)
|
||||
{
|
||||
m_iqrEncoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeEnd);
|
||||
boxContext.markOutputAsReadyToSend(4, boxContext.getInputChunkStartTime(0, j), boxContext.getInputChunkEndTime(0, j));
|
||||
}
|
||||
if (ip_isPercentileActive)
|
||||
{
|
||||
m_percentileEncoder->process(OVP_GD_Algorithm_StreamedMatrixEncoder_InputTriggerId_EncodeEnd);
|
||||
boxContext.markOutputAsReadyToSend(5, boxContext.getInputChunkStartTime(0, j), boxContext.getInputChunkEndTime(0, j));
|
||||
}
|
||||
}//end ender
|
||||
|
||||
boxContext.markInputAsDeprecated(0, j);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SignalProcessing {
|
||||
class CBoxUnivariateStatistic final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_UnivariateStatistic)
|
||||
|
||||
protected:
|
||||
|
||||
Kernel::IAlgorithmProxy* m_decoder = nullptr;
|
||||
Kernel::IAlgorithmProxy* m_meanEncoder = nullptr;
|
||||
Kernel::IAlgorithmProxy* m_varianceEncoder = nullptr;
|
||||
Kernel::IAlgorithmProxy* m_rangeEncoder = nullptr;
|
||||
Kernel::IAlgorithmProxy* m_medianEncoder = nullptr;
|
||||
Kernel::IAlgorithmProxy* m_iqrEncoder = nullptr;
|
||||
Kernel::IAlgorithmProxy* m_percentileEncoder = nullptr;
|
||||
Kernel::IAlgorithmProxy* m_matrixStatistic = nullptr;
|
||||
|
||||
Kernel::TParameterHandler<double> op_compression;
|
||||
Kernel::TParameterHandler<uint64_t> op_sampling;
|
||||
|
||||
Kernel::TParameterHandler<bool> ip_isMeanActive;
|
||||
Kernel::TParameterHandler<bool> ip_isVarianceActive;
|
||||
Kernel::TParameterHandler<bool> ip_isRangeActive;
|
||||
Kernel::TParameterHandler<bool> ip_isMedianActive;
|
||||
Kernel::TParameterHandler<bool> ip_isIQRActive;
|
||||
Kernel::TParameterHandler<bool> ip_isPercentileActive;
|
||||
Kernel::TParameterHandler<uint64_t> ip_parameterValue;
|
||||
|
||||
CIdentifier m_inputTypeID = CIdentifier::undefined();
|
||||
};
|
||||
|
||||
class CBoxUnivariateStatisticDesc final : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
CString getName() const override { return CString("Univariate Statistics"); }
|
||||
CString getAuthorName() const override { return CString("Matthieu Goyat"); }
|
||||
CString getAuthorCompanyName() const override { return CString("Gipsa-lab"); }
|
||||
CString getShortDescription() const override { return CString("Mean, Variance, Median, etc. on the incoming Signal"); }
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString("Signal processing/Statistics"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getStockItemName() const override { return CString("gtk-missing-image"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_UnivariateStatistic; }
|
||||
IPluginObject* create() override { return new CBoxUnivariateStatistic(); }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addInput("Input Signals", OV_TypeId_Signal);
|
||||
prototype.addOutput("Mean", OV_TypeId_Signal);
|
||||
prototype.addOutput("Variance", OV_TypeId_Signal);
|
||||
prototype.addOutput("Range", OV_TypeId_Signal);
|
||||
prototype.addOutput("Median", OV_TypeId_Signal);
|
||||
prototype.addOutput("IQR", OV_TypeId_Signal);
|
||||
prototype.addOutput("Percentile", OV_TypeId_Signal);
|
||||
prototype.addSetting("Mean", OV_TypeId_Boolean, "true");
|
||||
prototype.addSetting("Variance", OV_TypeId_Boolean, "true");
|
||||
prototype.addSetting("Range", OV_TypeId_Boolean, "true");
|
||||
prototype.addSetting("Median", OV_TypeId_Boolean, "true");
|
||||
prototype.addSetting("IQR", OV_TypeId_Boolean, "true");
|
||||
prototype.addSetting("Percentile", OV_TypeId_Boolean, "true");
|
||||
prototype.addSetting("Percentile value", OV_TypeId_Float, "30");
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_UnivariateStatisticDesc)
|
||||
};
|
||||
} // namespace SignalProcessing
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user