This commit is contained in:
2021-10-14 13:47:35 +02:00
commit 6625a8dfaa
4026 changed files with 844291 additions and 0 deletions
@@ -0,0 +1,54 @@
PROJECT(openvibe-plugins-sdk-stimulation)
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
SET(PROJECT_VERSION_MINOR ${OV_GLOBAL_VERSION_MINOR})
SET(PROJECT_VERSION_PATCH ${OV_GLOBAL_VERSION_PATCH})
SET(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.hpp src/*.inl)
INCLUDE("FindSourceRCProperties")
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("FindOpenViBEModuleSystem")
INCLUDE("FindOpenViBEModuleFS")
INCLUDE("FindThirdPartyBoost")
# ---------------------------------
# Finds standard library winmm
# Adds library to target
# Adds include path
# ---------------------------------
IF(WIN32)
FIND_LIBRARY(LIB_STANDARD_MODULE_WINMM winmm)
IF(LIB_STANDARD_MODULE_WINMM)
MESSAGE(STATUS " Found winmm...")
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${LIB_STANDARD_MODULE_WINMM})
ELSE(LIB_STANDARD_MODULE_WINMM)
MESSAGE(STATUS " FAILED to find winmm...")
ENDIF(LIB_STANDARD_MODULE_WINMM)
ENDIF(WIN32)
# ---------------------------------
# Target macros
# Defines target operating system, architecture and compiler
# ---------------------------------
SET_BUILD_PLATFORM()
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
@@ -0,0 +1,68 @@
/**
* \page BoxAlgorithm_ClockStimulator Clock stimulator
__________________________________________________________________
Detailed description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ClockStimulator_Description|
* This box produces stimulations at specific times depending on
* its configuration. The stimulation interval is fixed.
*
* It is important to underline that this box runs as fast as possible
* thus producting empty \ref Doc_Streams_Stimulation chunks when needed.
* |OVP_DocEnd_BoxAlgorithm_ClockStimulator_Description|
__________________________________________________________________
Outputs description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ClockStimulator_Outputs|
* |OVP_DocEnd_BoxAlgorithm_ClockStimulator_Outputs|
* |OVP_DocBegin_BoxAlgorithm_ClockStimulator_Output1|
* This output contains the generated stimulation stream.
* |OVP_DocEnd_BoxAlgorithm_ClockStimulator_Output1|
__________________________________________________________________
Settings description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ClockStimulator_Settings|
* |OVP_DocEnd_BoxAlgorithm_ClockStimulator_Settings|
* |OVP_DocBegin_BoxAlgorithm_ClockStimulator_Setting1|
* The first setting \f$ i \f$ informs the box how often it should produce stimulations, with \f$ i > 0 \f$.
* |OVP_DocEnd_BoxAlgorithm_ClockStimulator_Setting1|
* |OVP_DocBegin_BoxAlgorithm_ClockStimulator_Setting2|
* The second setting gives the stimulation code to trigger.
* |OVP_DocEnd_BoxAlgorithm_ClockStimulator_Setting2|
__________________________________________________________________
Examples description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ClockStimulator_Examples|
* In the designer, drag'n drop a \ref Dox_BoxAlgorithm_StimulationListener
* and connect it to a clock stimulator box. Configure it to your preferred
* active log level, press start and see what happens.
*
* The output should like this :
\verbatim
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33024 (0x8100)[OVTK_StimulationId_Label_00] at date 4294967296 (0x100000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33024 (0x8100)[OVTK_StimulationId_Label_00] at date 8589934592 (0x200000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33024 (0x8100)[OVTK_StimulationId_Label_00] at date 12884901888 (0x300000000) and duration 0 (0x0)
...
\endverbatim
* |OVP_DocEnd_BoxAlgorithm_ClockStimulator_Examples|
__________________________________________________________________
Miscellaneous description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_ClockStimulator_Miscellaneous|
* Notice that no stimulation is sent a time 0. The first stimulation to be sent
* has the inter stimulation duration as date.
* |OVP_DocEnd_BoxAlgorithm_ClockStimulator_Miscellaneous|
*/
@@ -0,0 +1,41 @@
/**
* \page BoxAlgorithm_PlayerController Player Controller
__________________________________________________________________
Detailed description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_PlayerController_Description|
* |OVP_DocEnd_BoxAlgorithm_PlayerController_Description|
__________________________________________________________________
Settings description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_PlayerController_Settings|
* |OVP_DocEnd_BoxAlgorithm_PlayerController_Settings|
*
* |OVP_DocBegin_BoxAlgorithm_PlayerController_Setting1|
* Input stimulation to react to
* |OVP_DocEnd_BoxAlgorithm_PlayerController_Setting1|
* |OVP_DocBegin_BoxAlgorithm_PlayerController_Setting2|
* Scenario player action to perform when stimulation is received. The options are 'forward', 'pause', 'play' and 'stop'.
* |OVP_DocEnd_BoxAlgorithm_PlayerController_Setting2|
__________________________________________________________________
Examples description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_PlayerController_Examples|
* |OVP_DocEnd_BoxAlgorithm_PlayerController_Examples|
__________________________________________________________________
Miscellaneous description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_PlayerController_Miscellaneous|
* |OVP_DocEnd_BoxAlgorithm_PlayerController_Miscellaneous|
*/
@@ -0,0 +1,93 @@
/**
* \page BoxAlgorithm_StimulationMultiplexer Stimulation multiplexer
__________________________________________________________________
Detailed description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_StimulationMultiplexer_Description|
* The Stimulation multiplexer box algorithm merges several stimulation streams
* into one stimulation stream. The contained stimulations are ordered
* according to their start date. Thus each time all the input have chunks
* covering a period of time, a new output chunk is sent. This box may eventually
* produce output chunk reflecting a different duration depending on the inputs.
* |OVP_DocEnd_BoxAlgorithm_StimulationMultiplexer_Description|
__________________________________________________________________
Inputs description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_StimulationMultiplexer_Inputs|
* This box can receive as many input as necessary. All the inputs
* will be of type \ref Doc_Streams_Stimulation in order to
* be parsed by this the reader. Every input have to be connected
* for this box to work correctly
* |OVP_DocEnd_BoxAlgorithm_StimulationMultiplexer_Inputs|
* |OVP_DocBegin_BoxAlgorithm_StimulationMultiplexer_Input1|
* This is the first default input of this box.
* |OVP_DocEnd_BoxAlgorithm_StimulationMultiplexer_Input1|
* |OVP_DocBegin_BoxAlgorithm_StimulationMultiplexer_Input2|
* This is the second default input of this box.
* |OVP_DocEnd_BoxAlgorithm_StimulationMultiplexer_Input2|
__________________________________________________________________
Outputs description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_StimulationMultiplexer_Outputs|
* |OVP_DocEnd_BoxAlgorithm_StimulationMultiplexer_Outputs|
* |OVP_DocBegin_BoxAlgorithm_StimulationMultiplexer_Output1|
* The output of this box is a new \ref Doc_Streams_Stimulation
* that contains all the stimulation of respective inputs. The
* input streams are multiplexed in a single stream ordering the
* stimulations according to their starting time.
* |OVP_DocEnd_BoxAlgorithm_StimulationMultiplexer_Output1|
__________________________________________________________________
Examples description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_StimulationMultiplexer_Examples|
* To illustrate this box, just drag'n drop a \ref Doc_BoxAlgorithm_KeyboardStimulator,
* a \ref Doc_BoxAlgorithm_ClockStimulator and a \ref Doc_BoxAlgorithm_StimulationListener
* box. Connect the two stimulation generators to your stimulation
* multiplexer and connect the output of the stimulation multiplexer
* to the stimulation listener. Leave the default parameters for all
* of those boxes, except the clock stimulator which you'll configure
* with an \e OVTK_StimulationId_Beep stimulation. Now pres 'start'.
*
* You should see something like this :
\verbatim
...
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33282 (0x8202)[OVTK_StimulationId_Beep] at date 4294967296 (0x100000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33025 (0x8101)[OVTK_StimulationId_Label_01] at date 8187281408 (0x1e8000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33282 (0x8202)[OVTK_StimulationId_Beep] at date 8589934592 (0x200000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33024 (0x8100)[OVTK_StimulationId_Label_00] at date 8992587776 (0x218000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33026 (0x8102)[OVTK_StimulationId_Label_02] at date 12213813248 (0x2d8000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33282 (0x8202)[OVTK_StimulationId_Beep] at date 12884901888 (0x300000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33282 (0x8202)[OVTK_StimulationId_Beep] at date 17179869184 (0x400000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33024 (0x8100)[OVTK_StimulationId_Label_00] at date 18924699648 (0x468000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33282 (0x8202)[OVTK_StimulationId_Beep] at date 21474836480 (0x500000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33027 (0x8103)[OVTK_StimulationId_Label_03] at date 23756537856 (0x588000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33024 (0x8100)[OVTK_StimulationId_Label_00] at date 24561844224 (0x5b8000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33282 (0x8202)[OVTK_StimulationId_Beep] at date 25769803776 (0x600000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33282 (0x8202)[OVTK_StimulationId_Beep] at date 30064771072 (0x700000000) and duration 0 (0x0)
...
\endverbatim
* In this session, I pressed \e z, \e z and \e a successively, causing
* an \e OVTK_StimulationId_Label_0x stimulation to be sent at key pressed
* time, and an \e OVTK_StimulationId_Label_00 stimulation to be sent at key
* release time. Inserted in those stimulations are several \e OVTK_StimulationId_Beep
* stimulations coming from the clock stimulator.
* |OVP_DocEnd_BoxAlgorithm_StimulationMultiplexer_Examples|
__________________________________________________________________
Miscellaneous description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_StimulationMultiplexer_Miscellaneous|
* |OVP_DocEnd_BoxAlgorithm_StimulationMultiplexer_Miscellaneous|
*/
@@ -0,0 +1,86 @@
/**
* \page BoxAlgorithm_StimulationVoter Stimulation Voter
__________________________________________________________________
Detailed description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_StimulationVoter_Description|
This box reads all the incoming stimuli a stimulus channel and returns the stimulus ID with the highest number of occurrences. Some constraints can be specified as box parameters.
* |OVP_DocEnd_BoxAlgorithm_StimulationVoter_Description|
__________________________________________________________________
Inputs description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_StimulationVoter_Inputs|
Collects the stimuli from a single input. To use several inputs, combine them first with Stimulation Multiplexer.
* |OVP_DocEnd_BoxAlgorithm_StimulationVoter_Inputs|
* |OVP_DocBegin_BoxAlgorithm_StimulationVoter_Input1|
Input stream, can include any stimuli.
* |OVP_DocEnd_BoxAlgorithm_StimulationVoter_Input1|
__________________________________________________________________
Outputs description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_StimulationVoter_Outputs|
* |OVP_DocEnd_BoxAlgorithm_StimulationVoter_Outputs|
* |OVP_DocBegin_BoxAlgorithm_StimulationVoter_Output1|
The output sends a new stimulation as soon as the most frequent stimulus type passes the criteria specified by the box parameters.
* |OVP_DocEnd_BoxAlgorithm_StimulationVoter_Output1|
__________________________________________________________________
Settings description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_StimulationVoter_Settings|
* |OVP_DocEnd_BoxAlgorithm_StimulationVoter_Settings|
* |OVP_DocBegin_BoxAlgorithm_StimulationVoter_Setting1|
Minimum number of incoming stimulus required to be inside the time window to carry out a vote.
* |OVP_DocEnd_BoxAlgorithm_StimulationVoter_Setting1|
* |OVP_DocBegin_BoxAlgorithm_StimulationVoter_Setting2|
The length of the time window. Stimuli are discarded after they drop outside the time window.
* |OVP_DocEnd_BoxAlgorithm_StimulationVoter_Setting2|
* |OVP_DocBegin_BoxAlgorithm_StimulationVoter_Setting3|
You can specify that stimuli can either vote as long as they remain inside the time window,
or that they are discarded after they participate in a single successful vote.
* |OVP_DocEnd_BoxAlgorithm_StimulationVoter_Setting3|
* |OVP_DocBegin_BoxAlgorithm_StimulationVoter_Setting4|
The output stimulus can be tagged with either the time of vote, the time of the last occurrence of the winning stimulus type, or the time of the last stimulus that participated in the vote.
* |OVP_DocEnd_BoxAlgorithm_StimulationVoter_Setting4|
* |OVP_DocBegin_BoxAlgorithm_StimulationVoter_Setting5|
Specifies the class interpreted as the 'rejected' class.
* |OVP_DocEnd_BoxAlgorithm_StimulationVoter_Setting5|
* |OVP_DocBegin_BoxAlgorithm_StimulationVoter_Setting6|
If set to 'no', 'rejected' class never wins the votes and is never returned as output.
* |OVP_DocEnd_BoxAlgorithm_StimulationVoter_Setting6|
__________________________________________________________________
Examples description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_StimulationVoter_Examples|
Possible usages include aggregating classifier outputs by making a majority vote over them.
* |OVP_DocEnd_BoxAlgorithm_StimulationVoter_Examples|
__________________________________________________________________
Miscellaneous description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_StimulationVoter_Miscellaneous|
This algorithm is similar to the Voting Classifier but it allows more parameters. Also, all stimulus available at time step <em>t</em> are used to carry out the vote at time step <em>t</em>. On the other hand, Stimulation Voter only supports input streams of type Stimulus.
* |OVP_DocEnd_BoxAlgorithm_StimulationVoter_Miscellaneous|
*/
@@ -0,0 +1,63 @@
/**
* \page BoxAlgorithm_Timeout Timeout
__________________________________________________________________
Detailed description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_Timeout_Description|
This box will send a stimulation on its output after it stops receiving an input. The user
can set a duration to wait before considering the output inactive.
* |OVP_DocEnd_BoxAlgorithm_Timeout_Description|
__________________________________________________________________
Inputs description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_Timeout_Inputs|
* |OVP_DocEnd_BoxAlgorithm_Timeout_Inputs|
* |OVP_DocBegin_BoxAlgorithm_Timeout_Input1|
Streamed Matrix input to be monitored.
* |OVP_DocEnd_BoxAlgorithm_Timeout_Input1|
__________________________________________________________________
Outputs description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_Timeout_Outputs|
* |OVP_DocEnd_BoxAlgorithm_Timeout_Outputs|
* |OVP_DocBegin_BoxAlgorithm_Timeout_Output1|
Output stimulation. One stimulation will be sent after the timeout is reached.
* |OVP_DocEnd_BoxAlgorithm_Timeout_Output1|
__________________________________________________________________
Settings description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_Timeout_Settings|
* |OVP_DocEnd_BoxAlgorithm_Timeout_Settings|
* |OVP_DocBegin_BoxAlgorithm_Timeout_Setting1|
Timeout value. Duration (in seconds) to wait after the last received chunk before the input is considered inactive.
* |OVP_DocEnd_BoxAlgorithm_Timeout_Setting1|
* |OVP_DocBegin_BoxAlgorithm_Timeout_Setting2|
Sitmulation ID to send.
* |OVP_DocEnd_BoxAlgorithm_Timeout_Setting2|
__________________________________________________________________
Examples description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_Timeout_Examples|
* |OVP_DocEnd_BoxAlgorithm_Timeout_Examples|
__________________________________________________________________
Miscellaneous description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_Timeout_Miscellaneous|
* |OVP_DocEnd_BoxAlgorithm_Timeout_Miscellaneous|
*/
@@ -0,0 +1,82 @@
.. _Doc_BoxAlgorithm_ClockStimulator:
Clock stimulator
================
.. container:: attribution
:Author:
Yann Renard
:Company:
INRIA/IRISA
.. image:: images/Doc_BoxAlgorithm_ClockStimulator.png
Triggers stimulation at fixed frequency
This box produces stimulations at specific times depending on
its configuration. The stimulation interval is fixed.
It is important to underline that this box runs as fast as possible
thus producing empty :ref:`Doc_Streams_Stimulation` chunks when needed.
Outputs
-------
.. csv-table::
:header: "Output Name", "Stream Type"
"Generated stimulations", "Stimulations"
Generated stimulations
~~~~~~~~~~~~~~~~~~~~~~
This output contains the generated stimulation stream.
.. _Doc_BoxAlgorithm_ClockStimulator_Settings:
Settings
--------
.. csv-table::
:header: "Setting Name", "Type", "Default Value"
"Interstimulation interval (in sec)", "Float", "1.0"
"Stimulation", "Stimulation", "OVTK_StimulationId_Label_00"
Interstimulation interval (in sec)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The first setting :math:`i` informs the box how often it should produce stimulations, with :math:`i > 0`.
Stimulation
~~~~~~~~~~~
The second setting gives the stimulation code to trigger.
.. _Doc_BoxAlgorithm_ClockStimulator_Examples:
Examples
--------
In the designer, drag'n drop a :ref:`Doc_BoxAlgorithm_StimulationListener`
and connect it to a clock stimulator box. Configure it to your preferred
active log level, press start and see what happens.
The output should like this :
.. code::
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33024 (0x8100)[OVTK_StimulationId_Label_00] at date 4294967296 (0x100000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33024 (0x8100)[OVTK_StimulationId_Label_00] at date 8589934592 (0x200000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33024 (0x8100)[OVTK_StimulationId_Label_00] at date 12884901888 (0x300000000) and duration 0 (0x0)
...
.. _Doc_BoxAlgorithm_ClockStimulator_Miscellaneous:
Miscellaneous
-------------
Notice that no stimulation is sent a time 0. The first stimulation to be sent
has the inter stimulation duration as date.
@@ -0,0 +1,57 @@
.. _Doc_BoxAlgorithm_PlayerController:
Player Controller
=================
.. container:: attribution
:Author:
Yann Renard
:Company:
INRIA
.. image:: images/Doc_BoxAlgorithm_PlayerController.png
This box can execute one of these actions on the current player instance on a trigger:
- Play
- Fast Forward
- Pause
- Stop
The box triggers upon receiving a specific stimulation on the Stimulations
input.
Due to the nature of the triggers, Play and Fast Forward actions can only be
used while the player is actually playing or in fast forward.
Inputs
------
.. csv-table::
:header: "Input Name", "Stream Type"
"Stimulations", "Stimulations"
.. _Doc_BoxAlgorithm_PlayerController_Settings:
Settings
--------
.. csv-table::
:header: "Setting Name", "Type", "Default Value"
"Stimulation name", "Stimulation", "OVTK_StimulationId_Label_00"
"Action to perform", "Player Action", "Pause"
Stimulation name
~~~~~~~~~~~~~~~~
Input stimulation to react to
Action to perform
~~~~~~~~~~~~~~~~~
Scenario player action to perform when stimulation is received. The options are 'forward', 'pause', 'play' and 'stop'.
@@ -0,0 +1,102 @@
.. _Doc_BoxAlgorithm_StimulationMultiplexer:
Stimulation multiplexer
=======================
.. container:: attribution
:Author:
Yann Renard
:Company:
INRIA/IRISA
.. image:: images/Doc_BoxAlgorithm_StimulationMultiplexer.png
The stimulations are ordered according to their start date. Thus each time all the input have chunks covering a period of time, a new output chunk is sent. This box may eventually produce output chunk reflecting a different duration depending on the inputs.
The Stimulation multiplexer box algorithm merges several stimulation streams
into one stimulation stream. The contained stimulations are ordered
according to their start date. Thus each time all the input have chunks
covering a period of time, a new output chunk is sent. This box may eventually
produce output chunk reflecting a different duration depending on the inputs.
Inputs
------
.. csv-table::
:header: "Input Name", "Stream Type"
"Input stimulations 1", "Stimulations"
"Input stimulations 2", "Stimulations"
This box can receive as many input as necessary. All the inputs
will be of type :ref:`Doc_Streams_Stimulation` in order to
be parsed by this the reader. Every input have to be connected
for this box to work correctly
Input stimulations 1
~~~~~~~~~~~~~~~~~~~~
This is the first default input of this box.
Input stimulations 2
~~~~~~~~~~~~~~~~~~~~
This is the second default input of this box.
Outputs
-------
.. csv-table::
:header: "Output Name", "Stream Type"
"Multiplexed stimulations", "Stimulations"
Multiplexed stimulations
~~~~~~~~~~~~~~~~~~~~~~~~
The output of this box is a new :ref:`Doc_Streams_Stimulation`
that contains all the stimulation of respective inputs. The
input streams are multiplexed in a single stream ordering the
stimulations according to their starting time.
.. _Doc_BoxAlgorithm_StimulationMultiplexer_Examples:
Examples
--------
To illustrate this box, just drag'n drop a :ref:`Doc_BoxAlgorithm_KeyboardStimulator`,
a :ref:`Doc_BoxAlgorithm_ClockStimulator` and a :ref:`Doc_BoxAlgorithm_StimulationListener`
box. Connect the two stimulation generators to your stimulation
multiplexer and connect the output of the stimulation multiplexer
to the stimulation listener. Leave the default parameters for all
of those boxes, except the clock stimulator which you'll configure
with an \e OVTK_StimulationId_Beep stimulation. Now pres 'start'.
You should see something like this :
.. code::
...
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33282 (0x8202)[OVTK_StimulationId_Beep] at date 4294967296 (0x100000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33025 (0x8101)[OVTK_StimulationId_Label_01] at date 8187281408 (0x1e8000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33282 (0x8202)[OVTK_StimulationId_Beep] at date 8589934592 (0x200000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33024 (0x8100)[OVTK_StimulationId_Label_00] at date 8992587776 (0x218000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33026 (0x8102)[OVTK_StimulationId_Label_02] at date 12213813248 (0x2d8000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33282 (0x8202)[OVTK_StimulationId_Beep] at date 12884901888 (0x300000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33282 (0x8202)[OVTK_StimulationId_Beep] at date 17179869184 (0x400000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33024 (0x8100)[OVTK_StimulationId_Label_00] at date 18924699648 (0x468000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33282 (0x8202)[OVTK_StimulationId_Beep] at date 21474836480 (0x500000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33027 (0x8103)[OVTK_StimulationId_Label_03] at date 23756537856 (0x588000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33024 (0x8100)[OVTK_StimulationId_Label_00] at date 24561844224 (0x5b8000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33282 (0x8202)[OVTK_StimulationId_Beep] at date 25769803776 (0x600000000) and duration 0 (0x0)
[ INF ] <Box algorithm::Stimulation listener> For input 0 (0x0) with name Stimulation stream 1 got stimulation 33282 (0x8202)[OVTK_StimulationId_Beep] at date 30064771072 (0x700000000) and duration 0 (0x0)
...
In this session, I pressed \e z, \e z and \e a successively, causing
an \e OVTK_StimulationId_Label_0x stimulation to be sent at key pressed
time, and an \e OVTK_StimulationId_Label_00 stimulation to be sent at key
release time. Inserted in those stimulations are several \e OVTK_StimulationId_Beep
stimulations coming from the clock stimulator.
@@ -0,0 +1,107 @@
.. _Doc_BoxAlgorithm_StimulationVoter:
Stimulation Voter
=================
.. container:: attribution
:Author:
Jussi T. Lindgren
:Company:
Inria
.. image:: images/Doc_BoxAlgorithm_StimulationVoter.png
Votes the most frequent stimulus ID in a given time window. Outputs the winning stimulus type. Several options are possible. To process multiple inputs, use Stimulation Multiplexer first.
This box reads all the incoming stimuli a stimulus channel and returns the stimulus ID with the highest number of occurrences. Some constraints can be specified as box parameters.
Inputs
------
.. csv-table::
:header: "Input Name", "Stream Type"
"Stimulus input", "Stimulations"
Collects the stimuli from a single input. To use several inputs, combine them first with Stimulation Multiplexer.
Stimulus input
~~~~~~~~~~~~~~
Input stream, can include any stimuli.
Outputs
-------
.. csv-table::
:header: "Output Name", "Stream Type"
"Selected stimulus", "Stimulations"
Selected stimulus
~~~~~~~~~~~~~~~~~
The output sends a new stimulation as soon as the most frequent stimulus type passes the criteria specified by the box parameters.
.. _Doc_BoxAlgorithm_StimulationVoter_Settings:
Settings
--------
.. csv-table::
:header: "Setting Name", "Type", "Default Value"
"Number of stimuli required for vote", "Integer", "4"
"Time window (secs)", "Float", "2"
"Clear votes", "Clear votes", "After output"
"Output timestamp", "Output time", "Time of last voting stimulus"
"Reject class label", "Stimulation", "OVTK_StimulationId_Label_00"
"Reject class can win", "Reject can win", "No"
Number of stimuli required for vote
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Minimum number of incoming stimulus required to be inside the time window to carry out a vote.
Time window (secs)
~~~~~~~~~~~~~~~~~~
The length of the time window. Stimuli are discarded after they drop outside the time window.
Clear votes
~~~~~~~~~~~
You can specify that stimuli can either vote as long as they remain inside the time window,
or that they are discarded after they participate in a single successful vote.
Output timestamp
~~~~~~~~~~~~~~~~
The output stimulus can be tagged with either the time of vote, the time of the last occurrence of the winning stimulus type, or the time of the last stimulus that participated in the vote.
Reject class label
~~~~~~~~~~~~~~~~~~
Specifies the class interpreted as the 'rejected' class.
Reject class can win
~~~~~~~~~~~~~~~~~~~~
If set to 'no', 'rejected' class never wins the votes and is never returned as output.
.. _Doc_BoxAlgorithm_StimulationVoter_Examples:
Examples
--------
Possible usages include aggregating classifier outputs by making a majority vote over them.
.. _Doc_BoxAlgorithm_StimulationVoter_Miscellaneous:
Miscellaneous
-------------
This algorithm is similar to the Voting Classifier but it allows more parameters. Also, all stimulus available at
time step *t* are used to carry out the vote at time step *t*. On the other hand, Stimulation Voter only supports input streams of type Stimulus.
@@ -0,0 +1,72 @@
.. _Doc_BoxAlgorithm_StreamEndDetector:
Stream End Detector
===================
.. container:: attribution
:Author:
Jozef Legeny
:Company:
Mensia Technologies
This box waits until it receives an End chunk in the incoming stream and sends
a stimulation once it receives it.
.. _Doc_BoxAlgorithm_StreamEndDetector_Inputs:
Inputs
------
This box receives a single input of arbitrary type derived from EBML. It does
not do any specific decoding.
.. csv-table::
:header: "Input Name", "Stream Type"
"EBML Stream", "EBML stream"
.. _Doc_BoxAlgorithm_StreamEndDetector_Outputs:
Outputs
-------
This box will output a single stimulation after receiving the End chunk. Before
outputting the stimulation, the stream will contain empty chunks in sync with
the input stream.
.. csv-table::
:header: "Output Name", "Stream Type"
"Output Stimulations", "Stimulations"
.. _Doc_BoxAlgorithm_StreamEndDetector_Settings:
Settings
--------
.. csv-table::
:header: "Setting Name", "Type", "Default Value"
"Stimulation name", "Stimulation", "OVTK_StimulationId_Label_00"
.. _Doc_BoxAlgorithm_StreamEndDetector_Setting_1:
Stimulation name
~~~~~~~~~~~~~~~~
Name or identifier of the stimulation to be sent by this box.
.. _Doc_BoxAlgorithm_StreamEndDetector_Examples:
Examples
--------
It can be combined with :ref:`Doc_BoxAlgorithm_PlayerController` to stop a
scenario on file end.
@@ -0,0 +1,66 @@
.. _Doc_BoxAlgorithm_Timeout:
Timeout
=======
.. container:: attribution
:Author:
Jozef Legény
:Company:
Inria
.. image:: images/Doc_BoxAlgorithm_Timeout.png
Sends a stimulation after a period of time without receiving signal. Useful for stopping scenarios after hardware disconnection.
This box will send a stimulation on its output after it stops receiving an input. The user
can set a duration to wait before considering the output inactive.
Inputs
------
.. csv-table::
:header: "Input Name", "Stream Type"
"Input Stream", "Streamed matrix"
Input Stream
~~~~~~~~~~~~
Streamed Matrix input to be monitored.
Outputs
-------
.. csv-table::
:header: "Output Name", "Stream Type"
"Output Stimulations", "Stimulations"
Output Stimulations
~~~~~~~~~~~~~~~~~~~
Output stimulation. One stimulation will be sent after the timeout is reached.
.. _Doc_BoxAlgorithm_Timeout_Settings:
Settings
--------
.. csv-table::
:header: "Setting Name", "Type", "Default Value"
"Timeout delay", "Integer", "5"
"Output Stimulation", "Stimulation", "OVTK_StimulationId_Label_00"
Timeout delay
~~~~~~~~~~~~~
Timeout value. Duration (in seconds) to wait after the last received chunk before the input is considered inactive.
Output Stimulation
~~~~~~~~~~~~~~~~~~
Sitmulation ID to send.
Binary file not shown.

After

Width:  |  Height:  |  Size: 790 B

@@ -0,0 +1,74 @@
#include "ovpCBoxAlgorithmClockStimulator.h"
namespace OpenViBE {
namespace Plugins {
namespace Stimulation {
bool CBoxAlgorithmClockStimulator::initialize()
{
const double interstimulationInterval = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
const double minInterstimulationInterval = 0.0001;
OV_ERROR_UNLESS_KRF(!(interstimulationInterval < minInterstimulationInterval),
"Invalid stimulation interval [" << interstimulationInterval << "] (expected value > " << minInterstimulationInterval << ")",
Kernel::ErrorType::BadSetting);
m_stimulationInterval = interstimulationInterval;
m_nSentStimulation = 0;
m_stimulationID = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
m_lastStimulationDate = 0;
m_lastEndTime = 0;
m_encoder.initialize(*this, 0);
return true;
}
bool CBoxAlgorithmClockStimulator::uninitialize()
{
m_encoder.uninitialize();
return true;
}
bool CBoxAlgorithmClockStimulator::processClock(Kernel::CMessageClock& /*msg*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
bool CBoxAlgorithmClockStimulator::process()
{
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
const uint64_t currentTime = getPlayerContext().getCurrentTime();
CStimulationSet stimulationSet;
stimulationSet.setStimulationCount(0);
while (CTime(double(m_nSentStimulation + 1) * m_stimulationInterval).time() < currentTime)
{
m_nSentStimulation += 1;
m_lastStimulationDate = CTime(double(m_nSentStimulation) * m_stimulationInterval).time();
stimulationSet.appendStimulation(m_stimulationID, m_lastStimulationDate, 0);
}
if (currentTime == 0)
{
m_encoder.encodeHeader();
boxContext.markOutputAsReadyToSend(0, m_lastEndTime, m_lastEndTime);
}
m_encoder.getInputStimulationSet() = &stimulationSet;
m_encoder.encodeBuffer();
boxContext.markOutputAsReadyToSend(0, m_lastEndTime, currentTime);
m_lastEndTime = currentTime;
return true;
}
} // namespace Stimulation
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,62 @@
#pragma once
#include "../ovp_defines.h"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
namespace OpenViBE {
namespace Plugins {
namespace Stimulation {
class CBoxAlgorithmClockStimulator final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
void release() override { delete this; }
uint64_t getClockFrequency() override { return (1LL << 32) * 32; }
bool initialize() override;
bool uninitialize() override;
bool processClock(Kernel::CMessageClock& msg) override;
bool process() override;
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_ClockStimulator)
protected:
Toolkit::TStimulationEncoder<CBoxAlgorithmClockStimulator> m_encoder;
uint64_t m_stimulationID = 0;
uint64_t m_lastStimulationDate = 0;
uint64_t m_lastEndTime = 0;
uint64_t m_nSentStimulation = 0;
double m_stimulationInterval = 0;
};
class CBoxAlgorithmClockStimulatorDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return CString("Clock stimulator"); }
CString getAuthorName() const override { return CString("Yann Renard"); }
CString getAuthorCompanyName() const override { return CString("INRIA/IRISA"); }
CString getShortDescription() const override { return CString("Periodic stimulation generator"); }
CString getDetailedDescription() const override { return CString("Triggers stimulation at fixed frequency"); }
CString getCategory() const override { return CString("Stimulation"); }
CString getVersion() const override { return CString("1.0"); }
CString getSoftwareComponent() const override { return CString("openvibe-sdk"); }
CString getAddedSoftwareVersion() const override { return CString("0.0.0"); }
CString getUpdatedSoftwareVersion() const override { return CString("0.0.0"); }
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_ClockStimulator; }
IPluginObject* create() override { return new CBoxAlgorithmClockStimulator; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addOutput("Generated stimulations", OV_TypeId_Stimulations);
prototype.addSetting("Interstimulation interval (in sec)", OV_TypeId_Float, "1.0");
prototype.addSetting("Stimulation", OV_TypeId_Stimulation, "OVTK_StimulationId_Label_00");
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_ClockStimulatorDesc)
};
} // namespace Stimulation
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,84 @@
#include "ovpCBoxAlgorithmPlayerController.h"
namespace OpenViBE {
namespace Plugins {
namespace Stimulation {
bool CBoxAlgorithmPlayerController::initialize()
{
m_stimulationID = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
m_actionID = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
m_decoder = &this->getAlgorithmManager().getAlgorithm(this->getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StimulationDecoder));
m_decoder->initialize();
ip_buffer.initialize(m_decoder->getInputParameter(OVP_GD_Algorithm_StimulationDecoder_InputParameterId_MemoryBufferToDecode));
op_stimulationSet.initialize(m_decoder->getOutputParameter(OVP_GD_Algorithm_StimulationDecoder_OutputParameterId_StimulationSet));
return true;
}
bool CBoxAlgorithmPlayerController::uninitialize()
{
op_stimulationSet.uninitialize();
ip_buffer.uninitialize();
if (m_decoder)
{
m_decoder->uninitialize();
this->getAlgorithmManager().releaseAlgorithm(*m_decoder);
m_decoder = nullptr;
}
return true;
}
bool CBoxAlgorithmPlayerController::processInput(const size_t /*index*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
bool CBoxAlgorithmPlayerController::process()
{
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
{
ip_buffer = boxContext.getInputChunk(0, i);
m_decoder->process();
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_StimulationDecoder_OutputTriggerId_ReceivedHeader)) { }
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_StimulationDecoder_OutputTriggerId_ReceivedBuffer))
{
IStimulationSet* stimSet = op_stimulationSet;
for (size_t j = 0; j < stimSet->getStimulationCount(); ++j)
{
if (stimSet->getStimulationIdentifier(j) == m_stimulationID)
{
this->getLogManager() << Kernel::LogLevel_Trace << "Received stimulation ["
<< this->getTypeManager().getEnumerationEntryNameFromValue(OV_TypeId_Stimulation, m_stimulationID) << "] causing action ["
<< this->getTypeManager().getEnumerationEntryNameFromValue(OV_TypeId_PlayerAction, m_actionID) << "]\n";
bool res = false;
if (m_actionID == OV_TypeId_PlayerAction_Play.id()) { res = this->getPlayerContext().play(); }
if (m_actionID == OV_TypeId_PlayerAction_Stop.id()) { res = this->getPlayerContext().stop(); }
if (m_actionID == OV_TypeId_PlayerAction_Pause.id()) { res = this->getPlayerContext().pause(); }
if (m_actionID == OV_TypeId_PlayerAction_Forward.id()) { res = this->getPlayerContext().forward(); }
OV_ERROR_UNLESS_KRF(res, "Failed to request player action ["
<< this->getTypeManager().getEnumerationEntryNameFromValue(OV_TypeId_PlayerAction, m_actionID) << "]",
Kernel::ErrorType::BadConfig);
}
}
}
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_StimulationDecoder_OutputTriggerId_ReceivedEnd)) { }
boxContext.markInputAsDeprecated(0, i);
}
return true;
}
} // namespace Stimulation
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,63 @@
#pragma once
#include "../ovp_defines.h"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
namespace OpenViBE {
namespace Plugins {
namespace Stimulation {
class CBoxAlgorithmPlayerController 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_PlayerController)
protected:
Kernel::IAlgorithmProxy* m_decoder = nullptr;
Kernel::TParameterHandler<const IMemoryBuffer*> ip_buffer;
Kernel::TParameterHandler<IStimulationSet*> op_stimulationSet;
uint64_t m_stimulationID = 0;
uint64_t m_actionID = 0;
};
class CBoxAlgorithmPlayerControllerDesc final : public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return CString("Player Controller"); }
CString getAuthorName() const override { return CString("Yann Renard"); }
CString getAuthorCompanyName() const override { return CString("INRIA"); }
CString getShortDescription() const override { return CString("Controls the player execution"); }
CString getDetailedDescription() const override { return CString("Add some settings to configure the way you want to control the player"); }
CString getCategory() const override { return CString("Stimulation"); }
CString getVersion() const override { return CString("1.0"); }
CString getSoftwareComponent() const override { return CString("openvibe-sdk"); }
CString getAddedSoftwareVersion() const override { return CString("0.0.0"); }
CString getUpdatedSoftwareVersion() const override { return CString("0.0.0"); }
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_PlayerController; }
IPluginObject* create() override { return new CBoxAlgorithmPlayerController; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("Stimulations", OV_TypeId_Stimulations);
prototype.addSetting("Stimulation name", OV_TypeId_Stimulation, "OVTK_StimulationId_Label_00");
prototype.addSetting("Action to perform", OV_TypeId_PlayerAction, OV_TypeId_PlayerAction_Pause.toString());
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_PlayerControllerDesc)
};
} // namespace Stimulation
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,112 @@
#include "ovpCBoxAlgorithmStimulationMultiplexer.h"
namespace OpenViBE {
namespace Plugins {
namespace Stimulation {
bool CBoxAlgorithmStimulationMultiplexer::initialize()
{
const Kernel::IBox& staticBoxContext = this->getStaticBoxContext();
m_decoders.resize(staticBoxContext.getInputCount());
size_t index = 0;
for (auto& stimulationDecoder : m_decoders)
{
stimulationDecoder.initialize(*this, index);
index += 1;
}
m_encoder.initialize(*this, 0);
m_decoderEndTimes = std::vector<uint64_t>(staticBoxContext.getInputCount(), 0ULL);
m_lastStartTime = 0;
m_lastEndTime = 0;
m_wasHeaderSent = false;
return true;
}
bool CBoxAlgorithmStimulationMultiplexer::uninitialize()
{
for (auto& stimulationDecoder : m_decoders) { stimulationDecoder.uninitialize(); }
m_encoder.uninitialize();
return true;
}
bool CBoxAlgorithmStimulationMultiplexer::processInput(const size_t /*index*/)
{
this->getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
bool CBoxAlgorithmStimulationMultiplexer::process()
{
const Kernel::IBox& staticBoxContext = this->getStaticBoxContext();
Kernel::IBoxIO& dynamicBoxContext = this->getDynamicBoxContext();
if (!m_wasHeaderSent)
{
m_encoder.encodeHeader();
dynamicBoxContext.markOutputAsReadyToSend(0, m_lastEndTime, m_lastEndTime);
m_wasHeaderSent = true;
}
uint64_t earliestReceivedChunkEndTime = 0xffffffffffffffffULL;
for (size_t input = 0; input < staticBoxContext.getInputCount(); ++input)
{
for (size_t chunk = 0; chunk < dynamicBoxContext.getInputChunkCount(input); ++chunk)
{
m_decoders[input].decode(chunk);
if (m_decoders[input].isBufferReceived())
{
for (size_t stimulation = 0; stimulation < m_decoders[input].getOutputStimulationSet()->getStimulationCount(); ++stimulation)
{
m_stimulations.insert(std::make_pair(m_decoders[input].getOutputStimulationSet()->getStimulationDate(stimulation),
std::make_tuple(
m_decoders[input].getOutputStimulationSet()->getStimulationIdentifier(stimulation),
m_decoders[input].getOutputStimulationSet()->getStimulationDate(stimulation),
m_decoders[input].getOutputStimulationSet()->getStimulationDuration(stimulation))));
}
}
m_decoderEndTimes[input] = dynamicBoxContext.getInputChunkEndTime(input, chunk);
}
if (earliestReceivedChunkEndTime > m_decoderEndTimes[input]) { earliestReceivedChunkEndTime = m_decoderEndTimes[input]; }
}
if (earliestReceivedChunkEndTime >= m_lastEndTime)
{
m_encoder.getInputStimulationSet()->clear();
for (auto stimulation = m_stimulations.begin(); stimulation != m_stimulations.end();)
{
if (stimulation->first < earliestReceivedChunkEndTime)
{
m_encoder.getInputStimulationSet()->appendStimulation(std::get<0>(stimulation->second), std::get<1>(stimulation->second),
std::get<2>(stimulation->second));
stimulation = m_stimulations.erase(stimulation);
}
else { ++stimulation; }
}
m_encoder.encodeBuffer();
dynamicBoxContext.markOutputAsReadyToSend(0, m_lastEndTime, earliestReceivedChunkEndTime);
m_lastStartTime = m_lastEndTime;
m_lastEndTime = earliestReceivedChunkEndTime;
}
return true;
}
} // namespace Stimulation
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,103 @@
#pragma once
#include "../ovp_defines.h"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <vector>
#include <map>
namespace OpenViBE {
namespace Plugins {
namespace Stimulation {
class CBoxAlgorithmStimulationMultiplexer 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_StimulationMultiplexer)
private:
std::vector<Toolkit::TStimulationDecoder<CBoxAlgorithmStimulationMultiplexer>> m_decoders;
Toolkit::TStimulationEncoder<CBoxAlgorithmStimulationMultiplexer> m_encoder;
std::vector<uint64_t> m_decoderEndTimes;
uint64_t m_lastStartTime = 0;
uint64_t m_lastEndTime = 0;
bool m_wasHeaderSent = false;
std::multimap<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> m_stimulations;
};
class CBoxAlgorithmStimulationMultiplexerListener final : public Toolkit::TBoxListener<IBoxListener>
{
public:
bool check(Kernel::IBox& box) const
{
for (size_t input = 0; input < box.getInputCount(); ++input)
{
box.setInputName(input, ("Input stimulations " + std::to_string(input + 1)).c_str());
box.setInputType(input, OV_TypeId_Stimulations);
}
return true;
}
bool onInputRemoved(Kernel::IBox& box, const size_t /*index*/) override { return this->check(box); }
bool onInputAdded(Kernel::IBox& box, const size_t /*index*/) override { return this->check(box); }
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
};
class CBoxAlgorithmStimulationMultiplexerDesc final : public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return CString("Stimulation multiplexer"); }
CString getAuthorName() const override { return CString("Yann Renard"); }
CString getAuthorCompanyName() const override { return CString("INRIA/IRISA"); }
CString getShortDescription() const override { return CString("Merges several stimulation streams into one."); }
CString getDetailedDescription() const override
{
return CString(
"The stimulations are ordered according to their start date. Thus each time all the input have chunks covering a period of time, a new output chunk is sent. This box may eventually produce output chunk reflecting a different duration depending on the inputs.");
}
CString getCategory() const override { return CString("Streaming"); }
CString getVersion() const override { return CString("1.1"); }
CString getStockItemName() const override { return CString("gtk-sort-ascending"); }
CString getSoftwareComponent() const override { return CString("openvibe-sdk"); }
CString getAddedSoftwareVersion() const override { return CString("0.0.0"); }
CString getUpdatedSoftwareVersion() const override { return CString("0.0.0"); }
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_StimulationMultiplexer; }
IPluginObject* create() override { return new CBoxAlgorithmStimulationMultiplexer; }
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmStimulationMultiplexerListener; }
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("Input stimulations 1", OV_TypeId_Stimulations);
prototype.addInput("Input stimulations 2", OV_TypeId_Stimulations);
prototype.addOutput("Multiplexed stimulations", OV_TypeId_Stimulations);
prototype.addFlag(Kernel::BoxFlag_CanAddInput);
prototype.addInputSupport(OV_TypeId_Stimulations);
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_StimulationMultiplexerDesc)
};
} // namespace Stimulation
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,209 @@
#include "ovpCBoxAlgorithmStimulationVoter.h"
#include <system/ovCMath.h>
#include <string>
#include <algorithm>
namespace OpenViBE {
namespace Plugins {
namespace Stimulation {
bool CBoxAlgorithmStimulationVoter::initialize()
{
const Kernel::IBox& boxContext = this->getStaticBoxContext();
OV_ERROR_UNLESS_KRF(boxContext.getInputCount() == 1, "Invalid number of inputs [" << boxContext.getInputCount() << "] (expected 1 single input)",
Kernel::ErrorType::BadInput);
CIdentifier typeID;
boxContext.getInputType(0, typeID);
OV_ERROR_UNLESS_KRF(typeID == OV_TypeId_Stimulations, "Invalid input type [" << typeID.str() << "] (expected OV_TypeId_Stimulations type)",
Kernel::ErrorType::BadInput);
m_encoder = &this->getAlgorithmManager().getAlgorithm(this->getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StimulationEncoder));
m_encoder->initialize();
m_decoder = &this->getAlgorithmManager().getAlgorithm(this->getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StimulationDecoder));
m_decoder->initialize();
ip_buffer.initialize(m_decoder->getInputParameter(OVP_GD_Algorithm_StimulationDecoder_InputParameterId_MemoryBufferToDecode));
op_stimulationSet.initialize(m_decoder->getOutputParameter(OVP_GD_Algorithm_StimulationDecoder_OutputParameterId_StimulationSet));
m_minimumVotes = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
m_timeWindow = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
m_clearVotes = uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2));
m_outputDateMode = uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 3));
m_rejectClassLabel = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 4);
m_rejectClassCanWin = uint64_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 5));
this->getLogManager() << Kernel::LogLevel_Debug << "Vote clear mode " << m_clearVotes << ", timestamp at " << m_outputDateMode << ", reject mode " <<
m_rejectClassCanWin << "\n";
m_latestStimulusDate = 0;
m_lastTime = 0;
m_oStimulusDeque.clear();
return true;
}
bool CBoxAlgorithmStimulationVoter::uninitialize()
{
m_decoder->uninitialize();
this->getAlgorithmManager().releaseAlgorithm(*m_decoder);
m_encoder->uninitialize();
this->getAlgorithmManager().releaseAlgorithm(*m_encoder);
return true;
}
bool CBoxAlgorithmStimulationVoter::processInput(const size_t /*index*/)
{
this->getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
bool CBoxAlgorithmStimulationVoter::process()
{
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
Kernel::TParameterHandler<IStimulationSet*> ip_stimSet(m_encoder->getInputParameter(OVP_GD_Algorithm_StimulationEncoder_InputParameterId_StimulationSet));
Kernel::TParameterHandler<IMemoryBuffer*> op_buffer(m_encoder->getOutputParameter(OVP_GD_Algorithm_StimulationEncoder_OutputParameterId_EncodedMemoryBuffer));
op_buffer = boxContext.getOutputChunk(0);
// Push the stimulations to a queue
bool newStimulus = false;
for (size_t j = 0; j < boxContext.getInputChunkCount(0); ++j)
{
ip_buffer = boxContext.getInputChunk(0, j);
m_decoder->process();
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_StimulationDecoder_OutputTriggerId_ReceivedHeader)) { }
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_StimulationDecoder_OutputTriggerId_ReceivedBuffer))
{
for (size_t k = 0; k < op_stimulationSet->getStimulationCount(); ++k)
{
uint64_t stimulationId = op_stimulationSet->getStimulationIdentifier(k);
uint64_t stimulationDate = op_stimulationSet->getStimulationDate(k);
m_latestStimulusDate = std::max(m_latestStimulusDate, stimulationDate);
if (CTime(m_latestStimulusDate - stimulationDate).toSeconds() <= m_timeWindow)
{
// Stimulus is fresh, append
m_oStimulusDeque.push_back(std::pair<uint64_t, uint64_t>(stimulationId, stimulationDate));
newStimulus = true;
}
}
}
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_StimulationDecoder_OutputTriggerId_ReceivedEnd)) { }
boxContext.markInputAsDeprecated(0, j);
}
if (m_oStimulusDeque.empty() || !newStimulus) { return true; }
// Always clear too old votes that have slipped off the time window. The time window is relative to the time of the latest stimulus received.
while (!m_oStimulusDeque.empty())
{
const uint64_t frontDate = m_oStimulusDeque.front().second;
if (CTime(m_latestStimulusDate - frontDate).toSeconds() > m_timeWindow)
{
// Drop it
m_oStimulusDeque.pop_front();
}
else
{
// Assume stimuli are received in time order. Since the stimulus at the head wasn't too old, the rest aren't either
break;
}
}
this->getLogManager() << Kernel::LogLevel_Debug << "Queue size is " << m_oStimulusDeque.size() << "\n";
if (m_oStimulusDeque.size() < m_minimumVotes)
{
// Not enough stimuli to vote
return true;
}
std::map<uint64_t, uint64_t> lastSeen; // The last occurrence of each type in time
std::map<uint64_t, size_t> votes; // Histogram of votes
// Make a histogram of the votes
for (const auto& stim : m_oStimulusDeque)
{
const uint64_t type = stim.first;
const uint64_t date = stim.second;
votes[type]++;
lastSeen[type] = std::max(lastSeen[type], date);
// Never pop here, only pop on expiration
}
// Find the winner
uint64_t resultClassLabel = m_rejectClassLabel;
size_t maxVotes = 0;
for (const auto& vote : votes)
{
const uint64_t type = vote.first;
const size_t nVotes = vote.second; // can not be zero by construction above
if (m_rejectClassCanWin == OVP_TypeId_Voting_RejectClass_CanWin_No && type == m_rejectClassLabel)
{
// Reject class never wins
continue;
}
if (nVotes > maxVotes)
{
resultClassLabel = type;
maxVotes = nVotes;
}
else if (nVotes == maxVotes)
{
// Break ties arbitrarily
if (System::Math::random0To1() > 0.5)
{
resultClassLabel = type;
maxVotes = nVotes;
}
}
}
if (m_lastTime == 0)
{
m_encoder->process(OVP_GD_Algorithm_StimulationEncoder_InputTriggerId_EncodeHeader);
boxContext.markOutputAsReadyToSend(0, m_lastTime, m_lastTime);
}
if (m_rejectClassCanWin == OVP_TypeId_Voting_RejectClass_CanWin_No && resultClassLabel == m_rejectClassLabel)
{
this->getLogManager() << Kernel::LogLevel_Debug << "Winning class " << resultClassLabel << " was 'rejected' with " << maxVotes << "votes. Dropped.\n";
}
else
{
const uint64_t currentTime = getPlayerContext().getCurrentTime();
uint64_t timeStamp;
if (m_outputDateMode == OVP_TypeId_Voting_OutputTime_Vote) { timeStamp = currentTime; }
else if (m_outputDateMode == OVP_TypeId_Voting_OutputTime_Winner) { timeStamp = lastSeen[resultClassLabel]; }
else { timeStamp = m_latestStimulusDate; }
this->getLogManager() << Kernel::LogLevel_Debug << "Appending winning stimulus " << resultClassLabel << " at " << timeStamp << " (" << maxVotes << " votes)\n";
ip_stimSet->setStimulationCount(0);
ip_stimSet->appendStimulation(resultClassLabel, timeStamp, 0);
m_encoder->process(OVP_GD_Algorithm_StimulationEncoder_InputTriggerId_EncodeBuffer);
boxContext.markOutputAsReadyToSend(0, m_lastTime, currentTime);
m_lastTime = currentTime;
if (m_clearVotes == OVP_TypeId_Voting_ClearVotes_AfterOutput) { m_oStimulusDeque.clear(); }
}
return true;
}
} // namespace Stimulation
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,104 @@
#pragma once
#include "../ovp_defines.h"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <map>
#include <deque>
namespace OpenViBE {
namespace Plugins {
namespace Stimulation {
class CBoxAlgorithmStimulationVoter 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_StimulationVoter)
protected:
uint64_t m_minimumVotes = 0;
double m_timeWindow = 0;
uint64_t m_rejectClassLabel = 0;
CIdentifier m_clearVotes = CIdentifier::undefined();
CIdentifier m_outputDateMode = CIdentifier::undefined();
CIdentifier m_rejectClassCanWin = CIdentifier::undefined();
private:
Kernel::IAlgorithmProxy* m_encoder = nullptr;
Kernel::IAlgorithmProxy* m_decoder = nullptr;
Kernel::TParameterHandler<const IMemoryBuffer*> ip_buffer;
Kernel::TParameterHandler<IStimulationSet*> op_stimulationSet;
std::deque<std::pair<uint64_t, uint64_t>> m_oStimulusDeque; // <label,time>
uint64_t m_latestStimulusDate = 0;
uint64_t m_lastTime = 0;
};
class CBoxAlgorithmStimulationVoterListener final : public Toolkit::TBoxListener<IBoxListener>
{
public:
CBoxAlgorithmStimulationVoterListener() : m_inputTypeID(OV_TypeId_Stimulations) { }
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
protected:
CIdentifier m_inputTypeID = CIdentifier::undefined();
};
class CBoxAlgorithmStimulationVoterDesc final : public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return CString("Stimulation Voter"); }
CString getAuthorName() const override { return CString("Jussi T. Lindgren"); }
CString getAuthorCompanyName() const override { return CString("Inria"); }
CString getShortDescription() const override { return CString("Performs majority vote on the input stimuli"); }
CString getDetailedDescription() const override
{
return CString(
"Votes the most frequent stimulus ID in a given time window. Outputs the winning stimulus type. Several options are possible. To process multiple inputs, use Stimulation Multiplexer first.");
}
CString getCategory() const override { return CString("Streaming"); }
CString getVersion() const override { return CString("1.0"); }
CString getSoftwareComponent() const override { return CString("openvibe-sdk"); }
CString getAddedSoftwareVersion() const override { return CString("0.0.0"); }
CString getUpdatedSoftwareVersion() const override { return CString("0.0.0"); }
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_StimulationVoter; }
IPluginObject* create() override { return new CBoxAlgorithmStimulationVoter; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("Stimulus input", OV_TypeId_Stimulations);
prototype.addOutput("Selected stimulus", OV_TypeId_Stimulations);
prototype.addSetting("Number of stimuli required for vote", OV_TypeId_Integer, "4");
prototype.addSetting("Time window (secs)", OV_TypeId_Float, "2");
prototype.addSetting("Clear votes", OVP_TypeId_Voting_ClearVotes, "After output");
prototype.addSetting("Output timestamp", OVP_TypeId_Voting_OutputTime, "Time of last voting stimulus");
prototype.addSetting("Reject class label", OV_TypeId_Stimulation, "OVTK_StimulationId_Label_00");
prototype.addSetting("Reject class can win", OVP_TypeId_Voting_RejectClass_CanWin, "No");
return true;
}
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmStimulationVoterListener; }
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_StimulationVoterDesc)
};
} // namespace Stimulation
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,100 @@
#include "ovpCBoxAlgorithmStreamEndDetector.h"
namespace OpenViBE {
namespace Plugins {
namespace Stimulation {
bool CBoxAlgorithmStreamEndDetector::initialize()
{
m_stimulationID = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), settingStimulationNameID());
OV_FATAL_UNLESS_K(this->getStaticBoxContext().getInterfacorIndex(Kernel::EBoxInterfacorType::Input, inputEBMLId(), m_inputEBMLIdx),
"Box does not have input with identifier " << inputEBMLId(), Kernel::ErrorType::Internal);
OV_FATAL_UNLESS_K(this->getStaticBoxContext().getInterfacorIndex(Kernel::EBoxInterfacorType::Output, outputStimulationsID(), m_outputStimulationsIdx),
"Box does not have output with identifier " << outputStimulationsID(), Kernel::ErrorType::Internal);
m_decoder.initialize(*this, m_inputEBMLIdx);
m_encoder.initialize(*this, m_outputStimulationsIdx);
m_isHeaderSent = false;
m_endDate = 0;
m_currentChunkEndDate = 0;
m_endState = EEndState::WaitingForEnd;
m_previousTime = 0;
return true;
}
bool CBoxAlgorithmStreamEndDetector::uninitialize()
{
m_decoder.uninitialize();
m_encoder.uninitialize();
return true;
}
bool CBoxAlgorithmStreamEndDetector::processInput(const size_t /*index*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
bool CBoxAlgorithmStreamEndDetector::process()
{
Kernel::IBoxIO& dynamicBoxContext = this->getDynamicBoxContext();
for (size_t chunk = 0; chunk < dynamicBoxContext.getInputChunkCount(m_inputEBMLIdx); ++chunk)
{
OV_ERROR_UNLESS_KRF(m_decoder.decode(chunk), "Failed to decode chunk", Kernel::ErrorType::Internal);
// We can not receive anything before this date anymore, thus we can send an empty stream
m_currentChunkEndDate = dynamicBoxContext.getInputChunkStartTime(m_inputEBMLIdx, chunk);
if (m_decoder.isEndReceived())
{
m_endState = EEndState::EndReceived;
m_endDate = dynamicBoxContext.getInputChunkEndTime(m_inputEBMLIdx, chunk);
// As this is the last chunk, we make it so it ends at the same time as the received End chunk
m_currentChunkEndDate = dynamicBoxContext.getInputChunkEndTime(m_inputEBMLIdx, chunk);
}
}
if (!m_isHeaderSent)
{
m_encoder.encodeHeader();
this->getDynamicBoxContext().markOutputAsReadyToSend(0, 0, 0);
m_isHeaderSent = true;
}
IStimulationSet* stimulationSet = m_encoder.getInputStimulationSet();
stimulationSet->clear();
// If the timeout is reached we send the stimulation on the output 0
if (m_endState == EEndState::EndReceived)
{
stimulationSet->appendStimulation(m_stimulationID, m_endDate, 0);
m_endState = EEndState::StimulationSent;
}
if (m_endState != EEndState::Finished && (m_endState == EEndState::StimulationSent || m_previousTime != m_currentChunkEndDate))
{
// we need to send an empty chunk even if there's no stim
m_encoder.encodeBuffer();
this->getDynamicBoxContext().markOutputAsReadyToSend(0, m_previousTime, m_currentChunkEndDate);
}
m_previousTime = m_currentChunkEndDate;
if (m_endState == EEndState::StimulationSent)
{
m_encoder.encodeEnd();
this->getDynamicBoxContext().markOutputAsReadyToSend(0, m_previousTime, m_previousTime);
m_endState = EEndState::Finished;
}
return true;
}
} // namespace Stimulation
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,81 @@
#pragma once
#include "../ovp_defines.h"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
namespace OpenViBE {
namespace Plugins {
namespace Stimulation {
class CBoxAlgorithmStreamEndDetector final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
static CIdentifier inputEBMLId() { return CIdentifier(0x0, 0x1); }
static CIdentifier outputStimulationsID() { return CIdentifier(0x1, 0x1); }
static CIdentifier settingStimulationNameID() { return CIdentifier(0x2, 0x1); }
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_StreamEndDetector)
protected:
Toolkit::TStreamStructureDecoder<CBoxAlgorithmStreamEndDetector> m_decoder;
Toolkit::TStimulationEncoder<CBoxAlgorithmStreamEndDetector> m_encoder;
uint64_t m_stimulationID = 0;
uint64_t m_actionID = 0;
private:
enum class EEndState
{
WaitingForEnd,
EndReceived,
StimulationSent,
Finished
};
uint64_t m_endDate = 0;
uint64_t m_currentChunkEndDate = 0;
uint64_t m_previousTime = 0;
size_t m_inputEBMLIdx = 0;
size_t m_outputStimulationsIdx = 0;
bool m_isHeaderSent = false;
EEndState m_endState = EEndState::WaitingForEnd;
};
class CBoxAlgorithmStreamEndDetectorDesc final : public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return CString("Stream End Detector"); }
CString getAuthorName() const override { return CString("Jozef Legeny"); }
CString getAuthorCompanyName() const override { return CString("Mensia Technologies"); }
CString getShortDescription() const override { return CString("Sends a stimulation upon receiving an End chunk"); }
CString getDetailedDescription() const override { return CString("Sends a stimulation upon receiving an End chunk"); }
CString getCategory() const override { return CString("Stimulation"); }
CString getVersion() const override { return CString("1.0"); }
CString getSoftwareComponent() const override { return CString("openvibe-sdk"); }
CString getAddedSoftwareVersion() const override { return CString("2.0.0"); }
CString getUpdatedSoftwareVersion() const override { return CString("0.0.0"); }
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_StreamEndDetector; }
IPluginObject* create() override { return new CBoxAlgorithmStreamEndDetector; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("EBML Stream", OV_TypeId_EBMLStream, CBoxAlgorithmStreamEndDetector::inputEBMLId());
prototype.addOutput("Output Stimulations", OV_TypeId_Stimulations, CBoxAlgorithmStreamEndDetector::outputStimulationsID());
prototype.addSetting("Stimulation name", OV_TypeId_Stimulation, "OVTK_StimulationId_Label_00", false,
CBoxAlgorithmStreamEndDetector::settingStimulationNameID());
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_StreamEndDetectorDesc)
};
} // namespace Stimulation
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,106 @@
#include "ovpCBoxAlgorithmTimeout.h"
#include <cmath>
namespace OpenViBE {
namespace Plugins {
namespace Stimulation {
bool CBoxAlgorithmTimeout::initialize()
{
m_timeoutState = ETimeoutState::No;
m_encoder.initialize(*this, 0);
const double timeout = double(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0));
OV_ERROR_UNLESS_KRF(timeout > 0, "Timeout delay value must be positive and non-zero", Kernel::ErrorType::BadSetting);
OV_ERROR_UNLESS_KRF(timeout == std::floor(timeout), "Timeout delay value is not an integer", Kernel::ErrorType::BadSetting);
m_timeout = uint64_t(timeout) << 32;
m_stimulationToSend = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
m_lastTimePolled = 0;
m_previousTime = 0;
m_isHeaderSent = false;
return true;
}
/*******************************************************************************/
bool CBoxAlgorithmTimeout::uninitialize()
{
m_encoder.uninitialize();
return true;
}
/*******************************************************************************/
bool CBoxAlgorithmTimeout::processClock(Kernel::CMessageClock& /*msg*/)
{
// if there was nothing received on the input for a period of time we raise the
// timeout flag and let the box send a stimulation
if (m_timeoutState == ETimeoutState::No && getPlayerContext().getCurrentTime() > m_lastTimePolled + m_timeout)
{
this->getLogManager() << Kernel::LogLevel_Trace << "Timeout reached at time " << CTime(this->getPlayerContext().getCurrentTime()) << "\n";
m_timeoutState = ETimeoutState::Occurred;
}
this->getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
/*******************************************************************************/
uint64_t CBoxAlgorithmTimeout::getClockFrequency() { return 16LL << 32; } // the box clock frequency
/*******************************************************************************/
bool CBoxAlgorithmTimeout::processInput(const size_t /*index*/)
{
this->getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
// every time we receive input we store the last kernel time
m_lastTimePolled = this->getPlayerContext().getCurrentTime();
return true;
}
/*******************************************************************************/
bool CBoxAlgorithmTimeout::process()
{
Kernel::IBoxIO& boxCtx = this->getDynamicBoxContext();
// Discard input data
for (size_t i = 0; i < boxCtx.getInputChunkCount(0); ++i) { boxCtx.markInputAsDeprecated(0, i); }
// Encoding the header
if (!m_isHeaderSent)
{
m_encoder.encodeHeader();
this->getDynamicBoxContext().markOutputAsReadyToSend(0, 0, 0);
m_isHeaderSent = true;
}
IStimulationSet* stimSet = m_encoder.getInputStimulationSet();
stimSet->clear();
const uint64_t date = this->getPlayerContext().getCurrentTime();
// If the timeout is reached we send the stimulation on the output 0
if (m_timeoutState == ETimeoutState::Occurred)
{
stimSet->appendStimulation(m_stimulationToSend, date, 0);
m_timeoutState = ETimeoutState::Sent;
}
// we need to send an empty chunk even if there's no stim
m_encoder.encodeBuffer();
this->getDynamicBoxContext().markOutputAsReadyToSend(0, m_previousTime, date);
m_previousTime = date;
return true;
}
} // namespace Stimulation
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,92 @@
#pragma once
#include "../ovp_defines.h"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
namespace OpenViBE {
namespace Plugins {
namespace Stimulation {
/**
* \class CBoxAlgorithmTimeout
* \author Jozef Legény (Inria)
* \date Thu Mar 21 14:40:23 2013
* \brief The class CBoxAlgorithmTimeout describes the box Timeout.
*
*/
class CBoxAlgorithmTimeout final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
void release() override { delete this; }
bool initialize() override;
bool uninitialize() override;
bool processClock(Kernel::CMessageClock& msg) override;
bool processInput(const size_t index) override;
uint64_t getClockFrequency() override;
bool process() override;
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_Timeout)
protected:
Toolkit::TStimulationEncoder<CBoxAlgorithmTimeout> m_encoder;
private:
enum class ETimeoutState { No, Occurred, Sent };
ETimeoutState m_timeoutState = ETimeoutState::No;
bool m_isHeaderSent = false;
uint64_t m_timeout = 0;
uint64_t m_lastTimePolled = 0;
uint64_t m_previousTime = 0;
uint64_t m_stimulationToSend = 0;
};
/**
* \class CBoxAlgorithmTimeoutDesc
* \author Jozef Legény (Inria)
* \date Thu Mar 21 14:40:23 2013
* \brief Descriptor of the box Timeout.
*
*/
class CBoxAlgorithmTimeoutDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return "Timeout"; }
CString getAuthorName() const override { return "Jozef Legény"; }
CString getAuthorCompanyName() const override { return "Inria"; }
CString getShortDescription() const override { return "Sends a stimulation after a period of time without receiving signal"; }
CString getDetailedDescription() const override
{
return "Sends a stimulation after a period of time without receiving signal. Useful for stopping scenarios after hardware disconnection.";
}
CString getCategory() const override { return "Stimulation"; }
CString getVersion() const override { return "1.1"; }
CString getSoftwareComponent() const override { return "openvibe-sdk"; }
CString getAddedSoftwareVersion() const override { return "0.0.0"; }
CString getUpdatedSoftwareVersion() const override { return "0.0.0"; }
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_Timeout; }
IPluginObject* create() override { return new CBoxAlgorithmTimeout; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("Input Stream",OV_TypeId_StreamedMatrix);
prototype.addOutput("Output Stimulations",OV_TypeId_Stimulations);
prototype.addSetting("Timeout delay",OV_TypeId_Integer, "5");
prototype.addSetting("Output Stimulation",OV_TypeId_Stimulation, "OVTK_StimulationId_Label_00");
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_TimeoutDesc)
};
} // namespace Stimulation
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,41 @@
#pragma once
// Boxes
//---------------------------------------------------------------------------------------------------
#define OVP_ClassId_BoxAlgorithm_ClockStimulator OpenViBE::CIdentifier(0x4F756D3F, 0x29FF0B96)
#define OVP_ClassId_BoxAlgorithm_ClockStimulatorDesc OpenViBE::CIdentifier(0x4FD067E9, 0x740D2AF0)
#define OVP_ClassId_BoxAlgorithm_PlayerController OpenViBE::CIdentifier(0x5F426DCE, 0x08456E13)
#define OVP_ClassId_BoxAlgorithm_PlayerControllerDesc OpenViBE::CIdentifier(0x2E4142D7, 0x3F957E25)
#define OVP_ClassId_BoxAlgorithm_StimulationMultiplexer OpenViBE::CIdentifier(0x07DB4EFA, 0x472B0938)
#define OVP_ClassId_BoxAlgorithm_StimulationMultiplexerDesc OpenViBE::CIdentifier(0x79EF4E4D, 0x178F09E6)
#define OVP_ClassId_BoxAlgorithm_StimulationVoter OpenViBE::CIdentifier(0x2BBD61FC, 0x041A4EDB)
#define OVP_ClassId_BoxAlgorithm_StimulationVoterDesc OpenViBE::CIdentifier(0x1C36287C, 0x6F143FBF)
#define OVP_ClassId_BoxAlgorithm_StreamEndDetector OpenViBE::CIdentifier(0x44F2725A, 0x8E922233)
#define OVP_ClassId_BoxAlgorithm_StreamEndDetectorDesc OpenViBE::CIdentifier(0x6DD8B6EA, 0xC581B3FC)
#define OVP_ClassId_BoxAlgorithm_Timeout OpenViBE::CIdentifier(0x24FCD292, 0x5C8F6AA8)
#define OVP_ClassId_BoxAlgorithm_TimeoutDesc OpenViBE::CIdentifier(0xB3D90844, 0x79A4CF6D)
// Type definitions
//---------------------------------------------------------------------------------------------------
#define OVP_TypeId_Voting_ClearVotes OpenViBE::CIdentifier(0x17AE30F8, 0x40B57661)
#define OVP_TypeId_Voting_ClearVotes_AfterOutput OpenViBE::CIdentifier(0x7FA81A20, 0x484023F9)
#define OVP_TypeId_Voting_ClearVotes_WhenExpires OpenViBE::CIdentifier(0x02766639, 0x00B155B4)
#define OVP_TypeId_Voting_OutputTime OpenViBE::CIdentifier(0x48583E8F, 0x47F22462)
#define OVP_TypeId_Voting_OutputTime_Vote OpenViBE::CIdentifier(0x2F37507F, 0x00C06761)
#define OVP_TypeId_Voting_OutputTime_Winner OpenViBE::CIdentifier(0x72416689, 0x17673658)
#define OVP_TypeId_Voting_OutputTime_Last OpenViBE::CIdentifier(0x4F2830DB, 0x716C2930)
#define OVP_TypeId_Voting_RejectClass_CanWin OpenViBE::CIdentifier(0x442F2F14, 0x7A17336C)
#define OVP_TypeId_Voting_RejectClass_CanWin_Yes OpenViBE::CIdentifier(0x40011974, 0x54BB3C71)
#define OVP_TypeId_Voting_RejectClass_CanWin_No OpenViBE::CIdentifier(0x275B746A, 0x480B302C)
#define OV_TypeId_PlayerAction OpenViBE::CIdentifier(0xCC14D8D6, 0xF27ECB73)
#define OV_TypeId_PlayerAction_Play OpenViBE::CIdentifier(0x6BD7E1B3, 0x85E75066)
#define OV_TypeId_PlayerAction_Stop OpenViBE::CIdentifier(0x8A72885E, 0xD537A9A4)
#define OV_TypeId_PlayerAction_Pause OpenViBE::CIdentifier(0xD2FA4573, 0xE7F1D11D)
#define OV_TypeId_PlayerAction_Forward OpenViBE::CIdentifier(0xE43E9888, 0x33A361ED)
// Global defines
//---------------------------------------------------------------------------------------------------
#ifdef TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
#include "ovp_global_defines.h"
#endif // TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
@@ -0,0 +1,46 @@
#include "ovp_defines.h"
#include "box-algorithms/ovpCBoxAlgorithmClockStimulator.h"
#include "box-algorithms/ovpCBoxAlgorithmPlayerController.h"
#include "box-algorithms/ovpCBoxAlgorithmStimulationMultiplexer.h"
#include "box-algorithms/ovpCBoxAlgorithmStimulationVoter.h"
#include "box-algorithms/ovpCBoxAlgorithmStreamEndDetector.h"
#include "box-algorithms/ovpCBoxAlgorithmTimeout.h"
namespace OpenViBE {
namespace Plugins {
namespace Stimulation {
OVP_Declare_Begin()
context.getTypeManager().registerEnumerationType(OV_TypeId_PlayerAction, "Player Action");
context.getTypeManager().registerEnumerationEntry(OV_TypeId_PlayerAction, "Play", OV_TypeId_PlayerAction_Play.id());
context.getTypeManager().registerEnumerationEntry(OV_TypeId_PlayerAction, "Stop", OV_TypeId_PlayerAction_Stop.id());
context.getTypeManager().registerEnumerationEntry(OV_TypeId_PlayerAction, "Pause", OV_TypeId_PlayerAction_Pause.id());
context.getTypeManager().registerEnumerationEntry(OV_TypeId_PlayerAction, "Forward", OV_TypeId_PlayerAction_Forward.id());
OVP_Declare_New(CBoxAlgorithmClockStimulatorDesc);
OVP_Declare_New(CBoxAlgorithmPlayerControllerDesc);
OVP_Declare_New(CBoxAlgorithmStimulationMultiplexerDesc);
OVP_Declare_New(CBoxAlgorithmStreamEndDetectorDesc);
OVP_Declare_New(CBoxAlgorithmTimeoutDesc);
OVP_Declare_New(CBoxAlgorithmStimulationVoterDesc);
context.getTypeManager().registerEnumerationType(OVP_TypeId_Voting_ClearVotes, "Clear votes");
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_Voting_ClearVotes, "When expires", OVP_TypeId_Voting_ClearVotes_WhenExpires.id());
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_Voting_ClearVotes, "After output", OVP_TypeId_Voting_ClearVotes_AfterOutput.id());
context.getTypeManager().registerEnumerationType(OVP_TypeId_Voting_OutputTime, "Output time");
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_Voting_OutputTime, "Time of voting", OVP_TypeId_Voting_OutputTime_Vote.id());
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_Voting_OutputTime, "Time of last winning stimulus", OVP_TypeId_Voting_OutputTime_Winner.id());
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_Voting_OutputTime, "Time of last voting stimulus", OVP_TypeId_Voting_OutputTime_Last.id());
context.getTypeManager().registerEnumerationType(OVP_TypeId_Voting_RejectClass_CanWin, "Reject can win");
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_Voting_RejectClass_CanWin, "Yes", OVP_TypeId_Voting_RejectClass_CanWin_Yes.id());
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_Voting_RejectClass_CanWin, "No", OVP_TypeId_Voting_RejectClass_CanWin_No.id());
OVP_Declare_End()
} // namespace Stimulation
} // namespace Plugins
} // namespace OpenViBE