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,33 @@
PROJECT(openvibe-plugins-tools)
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("FindOpenViBEModuleSystem")
INCLUDE("FindThirdPartyGTK")
INCLUDE("FindThirdPartyX11")
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(DIRECTORY share/ DESTINATION ${DIST_DATADIR}/openvibe/plugins/tools)
@@ -0,0 +1,58 @@
/**
* \page BoxAlgorithm_KeypressEmulator Keypress Emulator
__________________________________________________________________
Detailed description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_KeypressEmulator_Description|
Emulates a keyboard keypress when receiving a specific stimulation. In principle this box could be used to drive some 3rd party application that allows keyboard control but not any other communication from OpenViBE. Currently this box is implemented only on Windows.
* |OVP_DocEnd_BoxAlgorithm_KeypressEmulator_Description|
__________________________________________________________________
Inputs description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_KeypressEmulator_Inputs|
* |OVP_DocEnd_BoxAlgorithm_KeypressEmulator_Inputs|
* |OVP_DocBegin_BoxAlgorithm_KeypressEmulator_Input1|
Stimulation stream to listen to.
* |OVP_DocEnd_BoxAlgorithm_KeypressEmulator_Input1|
__________________________________________________________________
Settings description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_KeypressEmulator_Settings|
* |OVP_DocEnd_BoxAlgorithm_KeypressEmulator_Settings|
* |OVP_DocBegin_BoxAlgorithm_KeypressEmulator_Setting1|
The stimulation that will trigger the keypress.
* |OVP_DocEnd_BoxAlgorithm_KeypressEmulator_Setting1|
* |OVP_DocBegin_BoxAlgorithm_KeypressEmulator_Setting2|
The key to press.
* |OVP_DocEnd_BoxAlgorithm_KeypressEmulator_Setting2|
* |OVP_DocBegin_BoxAlgorithm_KeypressEmulator_Setting3|
The key modifier (alt, ctrl, ...)
* |OVP_DocEnd_BoxAlgorithm_KeypressEmulator_Setting3|
__________________________________________________________________
Examples description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_KeypressEmulator_Examples|
An easy way to test the box is to make it receive stimulations from a Clock Stimulator box, and make a text editor active on the desktop after clicking Play. The characters corresponding to the key should appear in the editor.
* |OVP_DocEnd_BoxAlgorithm_KeypressEmulator_Examples|
__________________________________________________________________
Miscellaneous description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_KeypressEmulator_Miscellaneous|
The Keypress Emulator should be enhanced to be able to map an arbitrary number of different stimulations. This development would be relatively straightforward (e.g. look at VRPN boxes for example). Meanwhile if you need several keys pressed, you can use several Keypress Emulator boxes, each configured for a different stimulation+key.
* |OVP_DocEnd_BoxAlgorithm_KeypressEmulator_Miscellaneous|
*/
@@ -0,0 +1,57 @@
/**
* \page BoxAlgorithm_MessageSpy EBML stream spy
__________________________________________________________________
Detailed description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_MessageSpy_Description|
* The purpose of this box is to spy a message connection and
* decode its contents to the log manager. The box loops over
* the possible types and keys inside a message and prints
* the output.
*
* This box is mostly useful for debug purpose. It allows
* a developper to check what arrives to a box in a human
* readable way.
* |OVP_DocEnd_BoxAlgorithm_MessageSpy_Description|
__________________________________________________________________
Inputs description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_MessageSpy_Inputs|
* This box can receive as many message inputs as necessary.
* |OVP_DocEnd_BoxAlgorithm_MessageSpy_Inputs|
* |OVP_DocBegin_BoxAlgorithm_MessageSpy_Input1|
* This is the default input of this box.
* |OVP_DocEnd_BoxAlgorithm_MessageSpy_Input1|
__________________________________________________________________
Settings description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_MessageSpy_Settings|
* |OVP_DocEnd_BoxAlgorithm_MessageSpy_Settings|
* |OVP_DocBegin_BoxAlgorithm_MessageSpy_Setting1|
* Indicates what log level will be used to
* print the message contents.
* |OVP_DocEnd_BoxAlgorithm_MessageSpy_Setting1|
__________________________________________________________________
Examples description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_MessageSpy_Examples|
There is a simple example scenario provided in box-tutorials/ called messaging.xml.
* |OVP_DocEnd_BoxAlgorithm_MessageSpy_Examples|
__________________________________________________________________
Miscellaneous description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_MessageSpy_Miscellaneous|
* |OVP_DocEnd_BoxAlgorithm_MessageSpy_Miscellaneous|
*/
@@ -0,0 +1,55 @@
/**
* \page BoxAlgorithm_MouseTracking Mouse Tracking
__________________________________________________________________
Detailed description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_MouseTracking_Description|
Opens a gtk window and track mouse within that window.
* |OVP_DocEnd_BoxAlgorithm_MouseTracking_Description|
__________________________________________________________________
Outputs description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_MouseTracking_Outputs|
* |OVP_DocEnd_BoxAlgorithm_MouseTracking_Outputs|
* |OVP_DocBegin_BoxAlgorithm_MouseTracking_Output1|
Output the raw coordinates of mouse position.
* |OVP_DocEnd_BoxAlgorithm_MouseTracking_Output1|
* |OVP_DocBegin_BoxAlgorithm_MouseTracking_Output2|
Output relative coordinates to previous position of the mouse.
* |OVP_DocEnd_BoxAlgorithm_MouseTracking_Output2|
__________________________________________________________________
Settings description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_MouseTracking_Settings|
* |OVP_DocEnd_BoxAlgorithm_MouseTracking_Settings|
* |OVP_DocBegin_BoxAlgorithm_MouseTracking_Setting1|
Sampling frequency.
* |OVP_DocEnd_BoxAlgorithm_MouseTracking_Setting1|
* |OVP_DocBegin_BoxAlgorithm_MouseTracking_Setting2|
Generated epoch sample count. Set the size of chunk. 1 in most cases : there is only one position at each time.
* |OVP_DocEnd_BoxAlgorithm_MouseTracking_Setting2|
__________________________________________________________________
Examples description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_MouseTracking_Examples|
* |OVP_DocEnd_BoxAlgorithm_MouseTracking_Examples|
__________________________________________________________________
Miscellaneous description
__________________________________________________________________
* |OVP_DocBegin_BoxAlgorithm_MouseTracking_Miscellaneous|
* |OVP_DocEnd_BoxAlgorithm_MouseTracking_Miscellaneous|
*/
@@ -0,0 +1,74 @@
OVTK_NodeId_Acquisition_Header EBML::CIdentifier(0x00000000, 0x00004239) master
OVTK_NodeId_Acquisition_AcquisitionInfo EBML::CIdentifier(0x00000000, 0x00004240) master
OVTK_NodeId_Acquisition_ExperimentId EBML::CIdentifier(0x00000000, 0x00004241) uinteger
OVTK_NodeId_Acquisition_SubjectAge EBML::CIdentifier(0x00000000, 0x00004242) uinteger
OVTK_NodeId_Acquisition_SubjectGender EBML::CIdentifier(0x00000000, 0x00004243) uinteger
OVTK_NodeId_Acquisition_ChannelCount EBML::CIdentifier(0x00000000, 0x00004244) uinteger
OVTK_NodeId_Acquisition_Sampling EBML::CIdentifier(0x00000000, 0x00004245) uinteger
OVTK_NodeId_Acquisition_ChannelNames EBML::CIdentifier(0x00000000, 0x00004246) master
OVTK_NodeId_Acquisition_ChannelName EBML::CIdentifier(0x00000000, 0x00004247) string
OVTK_NodeId_Acquisition_GainFactors EBML::CIdentifier(0x00000000, 0x00004248) master
OVTK_NodeId_Acquisition_GainFactor EBML::CIdentifier(0x00000000, 0x00004249) float
OVTK_NodeId_Acquisition_ChannelLocations EBML::CIdentifier(0x00000000, 0x00004250) master
OVTK_NodeId_Acquisition_ChannelLocation EBML::CIdentifier(0x00000000, 0x00004251) binary(double)
OVTK_NodeId_Acquisition_Buffer EBML::CIdentifier(0x00000000, 0x0000005A) master
OVTK_NodeId_Acquisition_Samples EBML::CIdentifier(0x00000000, 0x0000005B) master
OVTK_NodeId_Acquisition_SamplesPerChannelCount EBML::CIdentifier(0x00000000, 0x0000005C) uinteger
OVTK_NodeId_Acquisition_SampleBlock EBML::CIdentifier(0x00000000, 0x0000005D) binary(float)
OVTK_NodeId_Acquisition_Stimulations EBML::CIdentifier(0x00000000, 0x00000060) master
OVTK_NodeId_Acquisition_StimulationsCount EBML::CIdentifier(0x00000000, 0x00000061) uinteger
OVTK_NodeId_Acquisition_Stimulation EBML::CIdentifier(0x00000000, 0x00000062) master
OVTK_NodeId_Acquisition_StimulationSampleIndex EBML::CIdentifier(0x00000000, 0x00000063) uinteger
OVTK_NodeId_Acquisition_StimulationIdentifier EBML::CIdentifier(0x00000000, 0x00000064) uinteger
OVTK_NodeId_Header EBML::CIdentifier(0x002B395F, 0x108ADFAE) master
OVTK_NodeId_Header_StreamType EBML::CIdentifier(0x00CDD0F7, 0x46B0278D) uinteger
OVTK_NodeId_Header_StreamVersion EBML::CIdentifier(0x006F5A08, 0x7796EBC5) uinteger
OVTK_NodeId_Buffer EBML::CIdentifier(0x00CF2101, 0x02375310) master
OVTK_NodeId_End EBML::CIdentifier(0x00D9DDC3, 0x0B12873A) master
OVTK_NodeId_Header_StreamedMatrix EBML::CIdentifier(0x0072F560, 0x7ED2CBED) master
OVTK_NodeId_Header_StreamedMatrix_DimensionCount EBML::CIdentifier(0x003FEBD4, 0x2725D428) uinteger
OVTK_NodeId_Header_StreamedMatrix_Dimension EBML::CIdentifier(0x0000E3C0, 0x3A7D5141) master
OVTK_NodeId_Header_StreamedMatrix_Dimension_Size EBML::CIdentifier(0x001302F7, 0x36D8438A) uinteger
OVTK_NodeId_Header_StreamedMatrix_Dimension_Label EBML::CIdentifier(0x00153E40, 0x190227E0) string
OVTK_NodeId_Buffer_StreamedMatrix EBML::CIdentifier(0x00120663, 0x08FBC165) master
OVTK_NodeId_Buffer_StreamedMatrix_RawBuffer EBML::CIdentifier(0x00B18C10, 0x427D098C) binary(double)
OVTK_NodeId_Header_Signal EBML::CIdentifier(0x007855DE, 0x3748D375) master
OVTK_NodeId_Header_Signal_Sampling EBML::CIdentifier(0x00141C43, 0x0C37006B) uinteger
OVTK_NodeId_Header_Spectrum EBML::CIdentifier(0x00CCFA4B, 0x14F37D4D) master
OVTK_NodeId_Header_Spectrum_FrequencyBand EBML::CIdentifier(0x0010983C, 0x21F8BDE5) master
OVTK_NodeId_Header_Spectrum_FrequencyBand_Start EBML::CIdentifier(0x00AA5654, 0x2403A2CB) float
OVTK_NodeId_Header_Spectrum_FrequencyBand_Stop EBML::CIdentifier(0x00A44C82, 0x05BE50D5) float
OVTK_NodeId_Buffer_Stimulation EBML::CIdentifier(0x006DEABE, 0x7FC05A20) master
OVTK_NodeId_Buffer_Stimulation_NumberOfStimulations EBML::CIdentifier(0x00BB790B, 0x2B8574D8) uinteger
OVTK_NodeId_Buffer_Stimulation_Stimulation EBML::CIdentifier(0x0016EAC6, 0x29FBCAA1) master
OVTK_NodeId_Buffer_Stimulation_Stimulation_ID EBML::CIdentifier(0x006FA5DB, 0x4BAC31E9) uinteger
OVTK_NodeId_Buffer_Stimulation_Stimulation_Date EBML::CIdentifier(0x00B866D8, 0x14DA5374) uinteger
OVTK_NodeId_Buffer_Stimulation_Stimulation_Duration EBML::CIdentifier(0x14EE055F, 0x87FBCC9C) uinteger
OVTK_NodeId_Header_ChannelUnits EBML::CIdentifier(0x17400C76, 0x16CF14C8) master
OVTK_NodeId_Header_ChannelUnits_Dynamic EBML::CIdentifier(0x7307023C, 0x7F754D2E) uinteger
OVTK_NodeId_Header_ChannelLocalisation EBML::CIdentifier(0xF2CFE60B, 0xEFD63E3B) master
OVTK_NodeId_Header_ChannelLocalisation_Dynamic EBML::CIdentifier(0x5338AF5C, 0x07C469C3) uinteger
OVTK_NodeId_Header_ExperimentInfo EBML::CIdentifier(0x00746BA0, 0x115AE04D) master
OVTK_NodeId_Header_ExperimentInfo_Experiment EBML::CIdentifier(0x0011D6B7, 0x48F1AA39) master
OVTK_NodeId_Header_ExperimentInfo_Experiment_ID EBML::CIdentifier(0x006ACD74, 0x1C960C26) uinteger
OVTK_NodeId_Header_ExperimentInfo_Experiment_Date EBML::CIdentifier(0x002F8FB7, 0x6DA7552D) uinteger
OVTK_NodeId_Header_ExperimentInfo_Subject EBML::CIdentifier(0x003EC620, 0x333E0A94) master
OVTK_NodeId_Header_ExperimentInfo_Subject_ID EBML::CIdentifier(0x00D62974, 0x473D4AA5) uinteger
OVTK_NodeId_Header_ExperimentInfo_Subject_Name EBML::CIdentifier(0x0041FD0A, 0x6BCD9A99) string
OVTK_NodeId_Header_ExperimentInfo_Subject_Age EBML::CIdentifier(0x00DF7DD9, 0x33336C51) uinteger
OVTK_NodeId_Header_ExperimentInfo_Subject_Gender EBML::CIdentifier(0x0069BB84, 0x3FC8E149) uinteger
OVTK_NodeId_Header_ExperimentInfo_Context EBML::CIdentifier(0x0018C291, 0x7985DFDD) master
OVTK_NodeId_Header_ExperimentInfo_Context_LaboratoryID EBML::CIdentifier(0x003F11B9, 0x26D76D9C) uinteger
OVTK_NodeId_Header_ExperimentInfo_Context_LaboratoryName EBML::CIdentifier(0x00EB1F23, 0x51C23B83) string
OVTK_NodeId_Header_ExperimentInfo_Context_TechnicianID EBML::CIdentifier(0x00874A7F, 0x60DC34C2) uinteger
OVTK_NodeId_Header_ExperimentInfo_Context_TechnicianName EBML::CIdentifier(0x00C8C393, 0x31CE5B3E) string
DummyEnd EBML::CIdentifier(0x00000000, 0x00000000) binary
@@ -0,0 +1,158 @@
#include "ovpCBoxAlgorithmKeypressEmulator.h"
namespace OpenViBE {
namespace Plugins {
namespace Tools {
#if defined(TARGET_OS_Windows)
#include <winuser.h>
#endif
bool CBoxAlgorithmKeypressEmulator::initialize()
{
m_decoder.initialize(*this, 0);
m_triggerStimulation = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
m_keyToPress = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
#if defined(TARGET_OS_Windows)
const uint64_t modifier = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2);
if (modifier == OVP_TypeId_Keypress_Modifier_Shift.id()) { m_modifier = VK_SHIFT; }
else if (modifier == OVP_TypeId_Keypress_Modifier_Control.id()) { m_modifier = VK_CONTROL; }
else if (modifier == OVP_TypeId_Keypress_Modifier_Alt.id()) { m_modifier = VK_MENU; }
#elif defined(TARGET_OS_Linux)
// @todo implement the whole linux solution
// Handle modifier on linux here
getLogManager() << Kernel::LogLevel_Error << "This box is only implemented on Windows for the moment\n";
return false;
#endif
return true;
}
/*******************************************************************************/
bool CBoxAlgorithmKeypressEmulator::uninitialize()
{
m_decoder.uninitialize();
return true;
}
/*******************************************************************************/
bool CBoxAlgorithmKeypressEmulator::processInput(const size_t /*index*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
/*******************************************************************************/
bool CBoxAlgorithmKeypressEmulator::process()
{
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
{
m_decoder.decode(i);
if (m_decoder.isHeaderReceived()) { } // NOP
if (m_decoder.isBufferReceived())
{
const IStimulationSet* set = m_decoder.getOutputStimulationSet();
for (size_t s = 0; s < set->getStimulationCount(); ++s)
{
if (set->getStimulationIdentifier(s) == m_triggerStimulation)
{
#if defined(TARGET_OS_Windows)
getLogManager() << Kernel::LogLevel_Debug << "Received " << m_triggerStimulation << ", pressing Windows virtual key " << m_keyToPress
<< " with modifier " << m_modifier << ", delay is at least "
<< CTime(this->getPlayerContext().getCurrentTime() - set->getStimulationDate(s)) << "\n";
// @todo the stimulation time is not necessarily RIGHT NOW. A very
// accurate solution would call SendInput exactly when the key needs to be pressed. Unfortunately when
// we receive the stim chunk, the keypress is already old, and we can't press keys into the past. In this sense
// sending immediately might be the best we can do.
// Note that the speccing the "time" field of INPUT as nonzero doesn't send keypresses to the past or future.
if (m_modifier != 0)
{
INPUT keypress[3];
memset(keypress, 0, sizeof(INPUT) * 3);
keypress[0].type = INPUT_KEYBOARD;
keypress[0].ki.wVk = WORD(m_modifier);
keypress[1].type = INPUT_KEYBOARD;
keypress[1].ki.wVk = WORD(m_keyToPress);
keypress[2].type = INPUT_KEYBOARD;
keypress[2].ki.wVk = WORD(m_modifier);
keypress[2].ki.dwFlags = KEYEVENTF_KEYUP;
SendInput(3, keypress, sizeof(INPUT));
}
else
{
INPUT keypress;
memset(&keypress, 0, sizeof(INPUT));
keypress.type = INPUT_KEYBOARD;
keypress.ki.wVk = WORD(m_keyToPress);
SendInput(1, &keypress, sizeof(INPUT));
}
#elif defined(TARGET_OS_Linux)
// @todo
#endif
}
}
}
if (m_decoder.isEndReceived()) { } // NOP
}
return true;
}
void CBoxAlgorithmKeypressEmulator::registerEnums(const Kernel::IPluginModuleContext& ctx)
{
ctx.getTypeManager().registerEnumerationType(OVP_TypeId_Keypress_Modifier, "Key modifier");
ctx.getTypeManager().registerEnumerationEntry(OVP_TypeId_Keypress_Modifier, "None", OVP_TypeId_Keypress_Modifier_None.id());
ctx.getTypeManager().registerEnumerationEntry(OVP_TypeId_Keypress_Modifier, "Shift", OVP_TypeId_Keypress_Modifier_Shift.id());
ctx.getTypeManager().registerEnumerationEntry(OVP_TypeId_Keypress_Modifier, "Ctrl", OVP_TypeId_Keypress_Modifier_Control.id());
ctx.getTypeManager().registerEnumerationEntry(OVP_TypeId_Keypress_Modifier, "Alt", OVP_TypeId_Keypress_Modifier_Alt.id());
ctx.getTypeManager().registerEnumerationType(OVP_TypeId_Keypress_Key, "Key");
#if defined(TARGET_OS_Windows)
// @note this solution makes the scenario not portable between win<->linux. The challenge is keys such as F1 etc,
// otherwise we could just read ascii char and map it to a key.
// @fixme we're also not able to expose all the possible keys to the user this way, but the problem seems to be that
// GetKeyNameTextA does not give meaningful & different names to all keys for some reason.
// - The current appoarch attempts to expose the usual 'english' keys that can be visualized by pango.
// - We do not necessarily get arrow keys listed as these may not have a separate representation from
// the corresponding 'numpad' keys with the functions we call here; esp. GetKeyNameTextA seems to
// depend on the keyboard in use. This limits the scenario portability across keyboards.
for (int i = 0; i < 256; ++i)
{
const UINT mapped = MapVirtualKeyA(UINT(i), MAPVK_VK_TO_VSC);
const LONG scanCode = mapped << 16;
if (scanCode)
{
CHAR buffer[512];
if (GetKeyNameTextA(scanCode, buffer, 512))
{
std::string tmp(buffer);
if (tmp.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890_*&$%@^#!-+/\\ :;,.'`~()[]\"=") != std::string::npos)
{
// Some weird characters here, may be difficult to render for pango, ignore this key
continue;
}
// GetKeyNameTextA can return the same name for different keys. Only use the first one.
if (ctx.getTypeManager().getEnumerationEntryValueFromName(OVP_TypeId_Keypress_Key, buffer) == CIdentifier::undefined().id())
{
ctx.getTypeManager().registerEnumerationEntry(OVP_TypeId_Keypress_Key, buffer, i);
}
}
}
}
#elif defined(TARGET_OS_Linux)
// @todo register the linux keys to the enum here
#endif
}
} // namespace Tools
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,80 @@
#pragma once
#include "../ovp_defines.h"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
namespace OpenViBE {
namespace Plugins {
namespace Tools {
/**
* \class CBoxAlgorithmKeypressEmulator
* \author Jussi T. Lindgren / Inria
* \date 29.Oct.2019
* \brief Emulates keypresses on a keyboard based on input stimulations. Based on a request from Fabien Lotte / POTIOC.
*
*/
class CBoxAlgorithmKeypressEmulator final : virtual 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_KeypressEmulator)
// Register enums to the kernel used by this box
static void registerEnums(const Kernel::IPluginModuleContext& ctx);
protected:
Toolkit::TStimulationDecoder<CBoxAlgorithmKeypressEmulator> m_decoder;
// @todo for multiple triggers, use std::map<>
uint64_t m_triggerStimulation = 0;
uint64_t m_keyToPress = 0;
uint64_t m_modifier = 0;
};
class CBoxAlgorithmKeypressEmulatorDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return CString("Keypress Emulator"); }
CString getAuthorName() const override { return CString("Jussi T. Lindgren"); }
CString getAuthorCompanyName() const override { return CString("Inria"); }
CString getShortDescription() const override { return CString("Emulates pressing keyboard keys when receiving stimulations"); }
CString getDetailedDescription() const override { return CString(""); }
CString getCategory() const override { return CString("Tools"); }
CString getVersion() const override { return CString("0.1"); }
CString getStockItemName() const override { return CString("gtk-index"); }
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_KeypressEmulator; }
IPluginObject* create() override { return new CBoxAlgorithmKeypressEmulator; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("Stimulations",OV_TypeId_Stimulations);
// @todo add support for multiple keys, e.g. look at VRPN boxes for howto
prototype.addSetting("Trigger", OV_TypeId_Stimulation, "OVTK_StimulationId_Label_00");
prototype.addSetting("Key to press", OVP_TypeId_Keypress_Key, "A");
prototype.addSetting("Key modifier", OVP_TypeId_Keypress_Modifier, OVP_TypeId_Keypress_Modifier_None.toString());
prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_KeypressEmulatorDesc)
};
} // namespace Tools
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,56 @@
#include "ovpCBoxAlgorithmLatencyEvaluation.h"
#include <system/ovCTime.h>
// @note by just repeatedly printing to the console, this box introduces significant latency by itself.
// @fixme If it makes sense to enable this box at all, it should be reimplemented as printing to a small GUI widget instead.
namespace OpenViBE {
namespace Plugins {
namespace Tools {
bool CBoxAlgorithmLatencyEvaluation::initialize()
{
CString logLevel;
getBoxAlgorithmContext()->getStaticBoxContext()->getSettingValue(0, logLevel);
m_LogLevel = Kernel::ELogLevel(getBoxAlgorithmContext()->getPlayerContext()->getTypeManager().getEnumerationEntryValueFromName(OV_TypeId_LogLevel, logLevel));
m_StartTime = System::Time::zgetTime();
return true;
}
bool CBoxAlgorithmLatencyEvaluation::processInput(const size_t /*index*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
bool CBoxAlgorithmLatencyEvaluation::process()
{
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
const uint64_t time = getPlayerContext().getCurrentTime();
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
{
const uint64_t start = boxContext.getInputChunkStartTime(0, i);
const uint64_t end = boxContext.getInputChunkEndTime(0, i);
const double startLatency = (double((int64_t(time - start) >> 22) * 1000) / 1024.0);
const double endLatency = (double((int64_t(time - end) >> 22) * 1000) / 1024.0);
// getLogManager() << Kernel::LogLevel_Debug << "Timing values [start:end:current]=[" << tStart << ":" << tEnd << ":" << time << "]\n";
getLogManager() << m_LogLevel << "Current latency at chunk " << i << " [start:end]=[" << startLatency << ":" << endLatency << "] ms\n";
boxContext.markInputAsDeprecated(0, i);
}
const uint64_t elapsed = System::Time::zgetTime() - m_StartTime;
const double latency = (double((int64_t(elapsed - time) >> 22) * 1000) / 1024.0);
getLogManager() << m_LogLevel << "Inner latency : " << latency << " ms\n";
return true;
}
} // namespace Tools
} // 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 Tools {
class CBoxAlgorithmLatencyEvaluation final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
void release() override { delete this; }
bool initialize() override;
bool uninitialize() override { return true; }
bool processInput(const size_t index) override;
bool process() override;
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_LatencyEvaluation)
uint64_t m_StartTime = 0;
Kernel::ELogLevel m_LogLevel = Kernel::ELogLevel::LogLevel_None;
};
class CBoxAlgorithmLatencyEvaluationDesc final : public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return CString("Latency evaluation"); }
CString getAuthorName() const override { return CString("Yann Renard"); }
CString getAuthorCompanyName() const override { return CString("INRIA/IRISA"); }
CString getShortDescription() const override { return CString("Evaluates i/o jittering and outputs values to log manager"); }
CString getDetailedDescription() const override
{
return CString("This box evaluates i/o jittering comparing input chunk' start and end time against current clock time");
}
CString getCategory() const override { return CString("Tools"); }
CString getVersion() const override { return CString("1.0"); }
CString getStockItemName() const override { return CString("gtk-info"); }
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_LatencyEvaluation; }
IPluginObject* create() override { return new CBoxAlgorithmLatencyEvaluation; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addInput("input", OV_TypeId_EBMLStream);
prototype.addSetting("Log level to use", OV_TypeId_LogLevel, "Trace");
prototype.addFlag(Kernel::BoxFlag_CanModifyInput);
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_LatencyEvaluationDesc)
};
} // namespace Tools
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,159 @@
#if defined(TARGET_HAS_ThirdPartyGTK)
#include "ovpCBoxAlgorithmMouseTracking.h"
namespace OpenViBE {
namespace Plugins {
namespace Tools {
void motion_event_handler(GtkWidget* widget, GdkEventMotion* event, gpointer data);
bool CBoxAlgorithmMouseTracking::initialize()
{
// Feature vector stream encoder
m_algo0SignalEncoder.initialize(*this, 0);
// Feature vector stream encoder
m_algo1SignalEncoder.initialize(*this, 1);
m_algo0SignalEncoder.getInputMatrix().setReferenceTarget(m_absoluteCoordinateBuffer);
m_algo1SignalEncoder.getInputMatrix().setReferenceTarget(m_relativeCoordinateBuffer);
// Allocates coordinates Matrix
m_absoluteCoordinateBuffer = new CMatrix();
m_relativeCoordinateBuffer = new CMatrix();
// Retrieves settings
m_sampling = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
m_nGeneratedEpochSample = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
m_nSentSample = 0;
m_MouseX = 0;
m_MouseY = 0;
m_prevX = 0;
m_prevY = 0;
// Creates empty window to get mouse position
m_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_add_events(m_window, GDK_POINTER_MOTION_MASK);
gtk_window_maximize(GTK_WINDOW(m_window));
gtk_widget_show_all(m_window);
g_signal_connect(m_window, "motion-notify-event", G_CALLBACK(motion_event_handler), this);
m_headerSent = false;
m_algo0SignalEncoder.getInputSamplingRate() = m_sampling;
m_algo1SignalEncoder.getInputSamplingRate() = m_sampling;
return true;
}
/*******************************************************************************/
bool CBoxAlgorithmMouseTracking::uninitialize()
{
m_algo0SignalEncoder.uninitialize();
m_algo1SignalEncoder.uninitialize();
delete m_absoluteCoordinateBuffer;
m_absoluteCoordinateBuffer = nullptr;
delete m_relativeCoordinateBuffer;
m_relativeCoordinateBuffer = nullptr;
gtk_widget_destroy(m_window);
m_window = nullptr;
return true;
}
/*******************************************************************************/
bool CBoxAlgorithmMouseTracking::processClock(Kernel::CMessageClock& /*msg*/)
{
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
return true;
}
/*******************************************************************************/
uint64_t CBoxAlgorithmMouseTracking::getClockFrequency()
{
// Intentional parameter swap to get the frequency
m_clockFrequency = CTime(m_nGeneratedEpochSample, m_sampling).time();
return m_clockFrequency;
}
/*******************************************************************************/
bool CBoxAlgorithmMouseTracking::process()
{
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
//Send header and initialize Matrix
if (!m_headerSent)
{
m_absoluteCoordinateBuffer->resize(2, m_nGeneratedEpochSample);
m_absoluteCoordinateBuffer->setDimensionLabel(0, 0, "x");
m_absoluteCoordinateBuffer->setDimensionLabel(0, 1, "y");
m_relativeCoordinateBuffer->resize(2, m_nGeneratedEpochSample);
m_relativeCoordinateBuffer->setDimensionLabel(0, 0, "x");
m_relativeCoordinateBuffer->setDimensionLabel(0, 1, "y");
m_algo0SignalEncoder.encodeHeader();
m_algo1SignalEncoder.encodeHeader();
m_headerSent = true;
boxContext.markOutputAsReadyToSend(0, 0, 0);
boxContext.markOutputAsReadyToSend(1, 0, 0);
}
else //Do the process and send coordinates to output
{
const size_t nSentSample = m_nSentSample;
for (size_t i = 0; i < m_nGeneratedEpochSample; ++i)
{
m_absoluteCoordinateBuffer->getBuffer()[0 * m_nGeneratedEpochSample + i] = m_MouseX;
m_absoluteCoordinateBuffer->getBuffer()[1 * m_nGeneratedEpochSample + i] = m_MouseY;
m_relativeCoordinateBuffer->getBuffer()[0 * m_nGeneratedEpochSample + i] = m_MouseX - m_prevX;
m_relativeCoordinateBuffer->getBuffer()[1 * m_nGeneratedEpochSample + i] = m_MouseY - m_prevY;
}
m_prevX = m_MouseX;
m_prevY = m_MouseY;
m_nSentSample += m_nGeneratedEpochSample;
const uint64_t tStart = CTime(m_sampling, nSentSample).time();
const uint64_t tEnd = CTime(m_sampling, m_nSentSample).time();
m_algo0SignalEncoder.encodeBuffer();
m_algo1SignalEncoder.encodeBuffer();
boxContext.markOutputAsReadyToSend(0, tStart, tEnd);
boxContext.markOutputAsReadyToSend(1, tStart, tEnd);
}
return true;
}
// CALLBACK
// Get mouse position
void motion_event_handler(GtkWidget* /*widget*/, GdkEventMotion* event, gpointer data)
{
CBoxAlgorithmMouseTracking* box = static_cast<CBoxAlgorithmMouseTracking*>(data);
box->m_MouseX = double(event->x);
box->m_MouseY = double(event->y);
}
#endif
} // namespace Tools
} // namespace Plugins
} // namespace OpenViBE
@@ -0,0 +1,141 @@
#pragma once
#if defined(TARGET_HAS_ThirdPartyGTK)
#include "../ovp_defines.h"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <iostream>
#include <gtk/gtk.h>
namespace OpenViBE {
namespace Plugins {
namespace Tools {
/**
* \class CBoxAlgorithmMouseTracking
* \author Alison Cellard (Inria)
* \date Mon Mar 10 15:07:21 2014
* \brief The class CBoxAlgorithmMouseTracking describes the box Mouse tracking.
*
*/
class CBoxAlgorithmMouseTracking final : virtual public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
{
public:
// CBoxAlgorithmMouseTracking();
void release() override { delete this; }
bool initialize() override;
bool uninitialize() override;
bool processClock(Kernel::CMessageClock& msg) override;
uint64_t getClockFrequency() override;
bool process() override;
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_MouseTracking)
protected:
// Feature vector stream encoder
Toolkit::TSignalEncoder<CBoxAlgorithmMouseTracking> m_algo0SignalEncoder;
// Feature vector stream encoder
Toolkit::TSignalEncoder<CBoxAlgorithmMouseTracking> m_algo1SignalEncoder;
// To check if the header was sent or not
bool m_headerSent = false;
// Requested Sampling frequency
size_t m_sampling = 0;
// Process clock frequency
uint64_t m_clockFrequency = 0;
// Length of output chunks
size_t m_nGeneratedEpochSample = 0;
// Absolute coordinates of the mouse pointer, that is, relative to the window in fullscreen
CMatrix* m_absoluteCoordinateBuffer = nullptr;
// Relative coordinates of the mouse pointer, the coordinates is relative to the previous point
CMatrix* m_relativeCoordinateBuffer = nullptr;
size_t m_nSentSample = 0;
// Gtk window to track mouse position
GtkWidget* m_window = nullptr;
// X coordinate from the previous position (in pixel, reference is upper left corner of window)
double m_prevX = 0;
// Y coordinate from the previous position (in pixel, reference is upper left corner of window)
double m_prevY = 0;
public:
// X coordinate of mouse current position
double m_MouseX = 0;
// Y coordinate of mouse current position
double m_MouseY = 0;
};
class CBoxAlgorithmMouseTrackingListener final : public Toolkit::TBoxListener<IBoxListener>
{
public:
bool onSettingValueChanged(Kernel::IBox& /*box*/, const size_t /*index*/) override { return true; }
_IsDerivedFromClass_Final_(Toolkit::TBoxListener<IBoxListener>, CIdentifier::undefined())
};
/**
* \class CBoxAlgorithmMouseTrackingDesc
* \author Alison Cellard (Inria)
* \date Mon Mar 10 15:07:21 2014
* \brief Descriptor of the box Mouse tracking.
*
*/
class CBoxAlgorithmMouseTrackingDesc final : virtual public IBoxAlgorithmDesc
{
public:
void release() override { }
CString getName() const override { return CString("Mouse Tracking"); }
CString getAuthorName() const override { return CString("Alison Cellard"); }
CString getAuthorCompanyName() const override { return CString("Inria"); }
CString getShortDescription() const override { return CString("Track mouse position within the screen"); }
CString getDetailedDescription() const override { return CString("Return absolute and relative to the previous one mouse position"); }
CString getCategory() const override { return CString("Tools"); }
CString getVersion() const override { return CString("1"); }
CString getStockItemName() const override { return CString("gtk-index"); }
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_MouseTracking; }
IPluginObject* create() override { return new CBoxAlgorithmMouseTracking; }
IBoxListener* createBoxListener() const override { return new CBoxAlgorithmMouseTrackingListener; }
void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
prototype.addOutput("Absolute coordinate",OV_TypeId_Signal);
prototype.addOutput("Previous relative coordinate",OV_TypeId_Signal);
prototype.addSetting("Sampling Frequency",OV_TypeId_Integer, "16");
prototype.addSetting("Generated epoch sample count",OV_TypeId_Integer, "1");
prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
return true;
}
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_MouseTrackingDesc)
};
} // namespace Tools
} // namespace Plugins
} // namespace OpenViBE
#endif
@@ -0,0 +1,25 @@
#pragma once
// Boxes
//---------------------------------------------------------------------------------------------------
#define OVP_ClassId_BoxAlgorithm_LatencyEvaluation OpenViBE::CIdentifier(0x0AD11EC1, 0x7EF3690B)
#define OVP_ClassId_BoxAlgorithm_LatencyEvaluationDesc OpenViBE::CIdentifier(0x5DB56A54, 0x5380262B)
#define OVP_ClassId_BoxAlgorithm_MouseTracking OpenViBE::CIdentifier(0x1E386EE5, 0x203E13C6)
#define OVP_ClassId_BoxAlgorithm_MouseTrackingDesc OpenViBE::CIdentifier(0x7A31C11B, 0xF522262E)
#define OVP_ClassId_BoxAlgorithm_KeypressEmulator OpenViBE::CIdentifier(0x38503532, 0x19494145)
#define OVP_ClassId_BoxAlgorithm_KeypressEmulatorDesc OpenViBE::CIdentifier(0x59286224, 0x99423852)
// Global defines
//---------------------------------------------------------------------------------------------------
#ifdef TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
#include "ovp_global_defines.h"
#endif // TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
#define OV_AttributeId_Box_FlagIsUnstable OpenViBE::CIdentifier(0x666FFFFF, 0x666FFFFF)
#define OVP_TypeId_Keypress_Key OpenViBE::CIdentifier(0x23E525F9, 0x005F187D)
#define OVP_TypeId_Keypress_Modifier OpenViBE::CIdentifier(0x0C4F4585, 0x78B246AC)
#define OVP_TypeId_Keypress_Modifier_None OpenViBE::CIdentifier(0x5A70497E, 0x52F57199)
#define OVP_TypeId_Keypress_Modifier_Shift OpenViBE::CIdentifier(0x47534AA6, 0x138A1130)
#define OVP_TypeId_Keypress_Modifier_Control OpenViBE::CIdentifier(0x67C6455F, 0x08E6134D)
#define OVP_TypeId_Keypress_Modifier_Alt OpenViBE::CIdentifier(0x77577AEA, 0x74CD7E3E)
@@ -0,0 +1,29 @@
#include "ovp_defines.h"
//// #include "box-algorithms/ovpCBoxAlgorithmLatencyEvaluation.h"
#include "box-algorithms/ovpCBoxAlgorithmMouseTracking.h"
#include "box-algorithms/ovpCBoxAlgorithmKeypressEmulator.h"
#include "openvibe/CIdentifier.hpp"
namespace OpenViBE {
namespace Plugins {
namespace Tools {
OVP_Declare_Begin()
context.getTypeManager().registerEnumerationEntry(OV_TypeId_BoxAlgorithmFlag, OV_AttributeId_Box_FlagIsUnstable.toString(),
OV_AttributeId_Box_FlagIsUnstable.id());
// OVP_Declare_New(Tools::CBoxAlgorithmLatencyEvaluationDesc);
#if defined(TARGET_HAS_ThirdPartyGTK)
OVP_Declare_New(CBoxAlgorithmMouseTrackingDesc);
#endif
OVP_Declare_New(CBoxAlgorithmKeypressEmulatorDesc);
// @note the following code is a bit long so we've implemented it inside the class
CBoxAlgorithmKeypressEmulator::registerEnums(context);
OVP_Declare_End()
} // namespace Tools
} // namespace Plugins
} // namespace OpenViBE