init
This commit is contained in:
+118
@@ -0,0 +1,118 @@
|
||||
|
||||
IF(OV_DISABLE_GTK)
|
||||
MESSAGE(STATUS "Skipping Tracker, no GTK")
|
||||
RETURN()
|
||||
ENDIF(OV_DISABLE_GTK)
|
||||
|
||||
PROJECT(openvibe-tracker)
|
||||
|
||||
SET(PROJECT_VERSION ${OV_GLOBAL_VERSION_STRING})
|
||||
|
||||
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.inl include/*.h)
|
||||
|
||||
# @FIXME bad hack, we are cannibalizing a lot of non-exported code from Designer and Kernel
|
||||
# Its better than copy-paste but it'd be preferable to either export the necessary materials
|
||||
# or make another solution.
|
||||
SET(OV_AVP_PATH "${CMAKE_SOURCE_DIR}/../designer/plugins/visualization/ovp-advanced-visualization/src/")
|
||||
LIST(APPEND SRC_FILES ${OV_AVP_PATH}/m_defines.hpp
|
||||
${OV_AVP_PATH}/m_GtkGL.cpp
|
||||
${OV_AVP_PATH}/m_GtkGL.hpp
|
||||
${OV_AVP_PATH}/m_VisualizationTools.cpp
|
||||
${OV_AVP_PATH}/m_VisualizationTools.hpp
|
||||
${OV_AVP_PATH}/mTGtkGLWidget.hpp
|
||||
${OV_AVP_PATH}/mIRuler.hpp
|
||||
)
|
||||
|
||||
FILE(GLOB RULER_FILES RELATIVE ${OV_AVP_PATH}/ruler/ *.hpp)
|
||||
# MESSAGE(STATUS "FOUND ${RULER_FILES} in ${OV_AVP_PATH}")
|
||||
# AUX_SOURCE_DIRECTORY(${OV_AVP_PATH}/ruler RULER_FILES)
|
||||
LIST(APPEND SRC_FILES ${RULER_FILES})
|
||||
|
||||
# from wip-jlindgre-feature-renderer-offset
|
||||
ADD_DEFINITIONS("-DRENDERER_SUPPORTS_OFFSET")
|
||||
|
||||
SET(OV_DESIGNER_PATH "${CMAKE_SOURCE_DIR}/../designer/applications/platform/designer/src/")
|
||||
FILE(GLOB designer_files
|
||||
${OV_DESIGNER_PATH}/dynamic_settings/*.h ${OV_DESIGNER_PATH}/dynamic_settings/*.cpp
|
||||
${OV_DESIGNER_PATH}/ovd_base.h
|
||||
${OV_DESIGNER_PATH}/ovdCBoxConfigurationDialog.*
|
||||
${OV_DESIGNER_PATH}/ovdCLogListenerDesigner.*
|
||||
${OV_DESIGNER_PATH}/ovdCCommentEditorDialog.*
|
||||
${OV_DESIGNER_PATH}/ovdCSettingCollectionHelper.*
|
||||
)
|
||||
LIST(APPEND SRC_FILES ${designer_files})
|
||||
|
||||
configure_file(${OV_DESIGNER_PATH}/../share/interface.ui-base "${CMAKE_CURRENT_BINARY_DIR}/designer-interface.ui")
|
||||
configure_file(${OV_DESIGNER_PATH}/../share/interface-settings.ui-base "${CMAKE_CURRENT_BINARY_DIR}/designer-interface-settings.ui")
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/designer-interface.ui" DESTINATION ${DIST_DATADIR}/openvibe/applications/tracker)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/designer-interface-settings.ui" DESTINATION ${DIST_DATADIR}/openvibe/applications/tracker)
|
||||
|
||||
SET(OV_KERNEL_PATH "${CMAKE_SOURCE_DIR}/../sdk/kernel/src/kernel/")
|
||||
FILE(GLOB kernel_files
|
||||
${OV_KERNEL_PATH}/scenario/ovkCBoxProto.*
|
||||
${OV_KERNEL_PATH}/scenario/ovkTAttributable.h
|
||||
${OV_KERNEL_PATH}/ovkTKernelObject.h
|
||||
)
|
||||
LIST(APPEND SRC_FILES ${kernel_files})
|
||||
|
||||
ADD_EXECUTABLE(${PROJECT_NAME} ${SRC_FILES})
|
||||
SET_PROPERTY(TARGET ${PROJECT_NAME} PROPERTY FOLDER ${APP_FOLDER})
|
||||
|
||||
INCLUDE_DIRECTORIES("include")
|
||||
INCLUDE_DIRECTORIES(${OV_AVP_PATH})
|
||||
INCLUDE_DIRECTORIES(${OV_DESIGNER_PATH})
|
||||
INCLUDE_DIRECTORIES(${OV_KERNEL_PATH})
|
||||
INCLUDE_DIRECTORIES(${OV_KERNEL_PATH}/scenario)
|
||||
|
||||
INCLUDE("FindOpenViBE")
|
||||
INCLUDE("FindOpenViBECommon")
|
||||
INCLUDE("FindOpenViBEToolkit")
|
||||
INCLUDE("FindOpenViBEModuleEBML")
|
||||
INCLUDE("FindOpenViBEModuleSocket")
|
||||
INCLUDE("FindOpenViBEModuleSystem")
|
||||
INCLUDE("FindOpenViBEModuleFS")
|
||||
INCLUDE("FindOpenViBEModuleMessaging")
|
||||
INCLUDE("FindOpenViBEModuleXML")
|
||||
INCLUDE("FindThirdPartyGTK")
|
||||
INCLUDE("FindThirdPartyBoost_System")
|
||||
INCLUDE("FindThirdPartyOpenGL")
|
||||
INCLUDE("FindOpenViBEVisualizationToolkit")
|
||||
INCLUDE("FindLibMensiaAdvancedVisualisation")
|
||||
|
||||
IF(WIN32)
|
||||
# @FIXME CERT getting timeBeginPeriod() linker issues without this
|
||||
TARGET_LINK_LIBRARIES(${PROJECT_NAME} winmm)
|
||||
ENDIF(WIN32)
|
||||
|
||||
INCLUDE("FindThirdPartyBoost")
|
||||
INCLUDE("FindThirdPartyRT") # external stims shared memory needs this
|
||||
|
||||
# ---------------------------------
|
||||
# Target macros
|
||||
# Defines target operating system, architecture and compiler
|
||||
# ---------------------------------
|
||||
IF(WIN32)
|
||||
ADD_DEFINITIONS(-DWIN32_LEAN_AND_MEAN)
|
||||
ENDIF(WIN32)
|
||||
|
||||
# ----------------------
|
||||
# Generate launch script
|
||||
# ----------------------
|
||||
OV_INSTALL_LAUNCH_SCRIPT(SCRIPT_PREFIX "${PROJECT_NAME}" EXECUTABLE_NAME "${PROJECT_NAME}")
|
||||
|
||||
# -----------------------------
|
||||
# Install files
|
||||
# -----------------------------
|
||||
INSTALL(TARGETS ${PROJECT_NAME}
|
||||
RUNTIME DESTINATION ${DIST_BINDIR}
|
||||
LIBRARY DESTINATION ${DIST_LIBDIR}
|
||||
ARCHIVE DESTINATION ${DIST_LIBDIR})
|
||||
|
||||
configure_file(share/tracker.ui-base "${CMAKE_CURRENT_BINARY_DIR}/tracker.ui" @ONLY)
|
||||
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/tracker.ui" DESTINATION ${DIST_DATADIR}/openvibe/applications/tracker)
|
||||
|
||||
INSTALL(DIRECTORY signals DESTINATION ${DIST_DATADIR}/openvibe/scenarios/)
|
||||
INSTALL(DIRECTORY share/ DESTINATION ${DIST_DATADIR}/openvibe/applications/tracker PATTERN "*-base" EXCLUDE)
|
||||
INSTALL(DIRECTORY workspaces DESTINATION ${DIST_DATADIR}/openvibe/scenarios/)
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
|
||||
Nothing for now. See git history for previous ideas.
|
||||
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "Contexted.h"
|
||||
|
||||
#include "StreamBundle.h"
|
||||
|
||||
#include "BoxAdapterHelper.h"
|
||||
|
||||
#include "Encoder.h"
|
||||
#include "Decoder.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class BoxAdapter
|
||||
* \brief A partially abstract class allowing processing data with OpenViBE boxes using a simple interface.
|
||||
*
|
||||
* The class implements a few of its members, but derived classes should implement the method spool() that feeds the data to the box,
|
||||
* as well as members allowing setting the sources and destinations of the data.
|
||||
*
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class BoxAdapter : protected Contexted
|
||||
{
|
||||
public:
|
||||
|
||||
BoxAdapter(const Kernel::IKernelContext& ctx, const CIdentifier& algorithmId)
|
||||
: Contexted(ctx), m_boxAlgorithmCtx(ctx), m_algorithmID(algorithmId) { }
|
||||
|
||||
~BoxAdapter() override { }
|
||||
|
||||
virtual bool initialize();
|
||||
virtual bool spool(const bool verbose) = 0;
|
||||
virtual bool uninitialize();
|
||||
|
||||
virtual TrackerBox& getBox() { return m_boxAlgorithmCtx.m_StaticBoxCtx; }
|
||||
virtual CIdentifier getAlgorithmId() { return m_algorithmID; }
|
||||
|
||||
protected:
|
||||
TrackerBoxAlgorithmContext m_boxAlgorithmCtx;
|
||||
Plugins::IBoxAlgorithm* m_boxAlgorithm = nullptr;
|
||||
CIdentifier m_algorithmID = CIdentifier::undefined();
|
||||
};
|
||||
|
||||
/**
|
||||
* \class BoxAdapterStream
|
||||
* \brief A box adapter that reads from a Stream and writes to another Stream, having effect equivalent to outStream = process(inStream);
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class BoxAdapterStream final : public BoxAdapter
|
||||
{
|
||||
public:
|
||||
|
||||
BoxAdapterStream(const Kernel::IKernelContext& ctx, const CIdentifier& algorithmId) : BoxAdapter(ctx, algorithmId) { }
|
||||
|
||||
void setSource(const StreamPtr& source) { m_src = source; }
|
||||
void setTarget(const StreamPtr& target) { m_dst = target; }
|
||||
|
||||
bool initialize() override;
|
||||
|
||||
// Process the whole stream from source to target
|
||||
bool spool(const bool verbose) override;
|
||||
|
||||
protected:
|
||||
StreamPtr m_src = nullptr, m_dst = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class BoxAdapterBundle
|
||||
* \brief A box adapter that reads from a StreamBundle and writes to another StreamBundle
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class BoxAdapterBundle : public BoxAdapter
|
||||
{
|
||||
public:
|
||||
|
||||
BoxAdapterBundle(const Kernel::IKernelContext& ctx, const CIdentifier& algorithmId) : BoxAdapter(ctx, algorithmId) { }
|
||||
|
||||
~BoxAdapterBundle() override;
|
||||
|
||||
virtual void setSource(StreamBundle* source) { m_src = source; }
|
||||
virtual void setTarget(StreamBundle* target) { m_dst = target; }
|
||||
|
||||
bool initialize() override;
|
||||
|
||||
// Process the whole bundle from source to target
|
||||
bool spool(const bool verbose) override;
|
||||
|
||||
protected:
|
||||
StreamBundle *m_src = nullptr, *m_dst = nullptr;
|
||||
std::vector<EncoderBase*> m_encoders;
|
||||
std::vector<DecoderBase*> m_decoders;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "BoxAdapter.h"
|
||||
|
||||
static const OpenViBE::CIdentifier STREAM_WRITER_ID = OpenViBE::CIdentifier(0x09C92218, 0x7C1216F8);
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class BoxAdapterGenericStreamWriter
|
||||
* \brief A specific wrapper for Generic Stream Writer allowing to write tracks as .ov files from the Tracker
|
||||
* \details
|
||||
* This class can be considered as the counterpart of the Demuxer class, except here we simply wrap the Generic Stream Writer
|
||||
* code instead of developing a new class.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class BoxAdapterGenericStreamWriter final : public BoxAdapterBundle
|
||||
{
|
||||
public:
|
||||
|
||||
BoxAdapterGenericStreamWriter(const Kernel::IKernelContext& ctx, StreamBundle& source, const std::string& filename)
|
||||
: BoxAdapterBundle(ctx, STREAM_WRITER_ID), m_filename(filename) { BoxAdapterBundle::setSource(&source); }
|
||||
|
||||
bool initialize() override
|
||||
{
|
||||
// @fixme these should actually come from the box description
|
||||
Kernel::IBox* boxCtx = const_cast<Kernel::IBox*>(m_boxAlgorithmCtx.getStaticBoxContext());
|
||||
boxCtx->addSetting("Filename", 0, CString(m_filename.c_str()));
|
||||
boxCtx->addSetting("Compress", 0, "false");
|
||||
|
||||
BoxAdapterBundle::initialize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
std::string m_filename;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+761
@@ -0,0 +1,761 @@
|
||||
/*
|
||||
* This file implements various contexts we need in order to run a box.
|
||||
*
|
||||
* @fixme most of the implementation is very primitive and may be even missing altogether
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <stack>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <openvibe/CMatrix.hpp>
|
||||
|
||||
#include <fs/Files.h>
|
||||
|
||||
#include <ebml/CReader.h>
|
||||
#include <ebml/CReaderHelper.h>
|
||||
|
||||
#include <algorithm> // std::find on Ubuntu
|
||||
|
||||
#include "Contexted.h"
|
||||
#include "EncodedChunk.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TrackerPlayerContext
|
||||
* \brief Implements Kernel::IPlayerContext
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TrackerPlayerContext final : protected Contexted, public Kernel::IPlayerContext
|
||||
{
|
||||
public:
|
||||
explicit TrackerPlayerContext(const Kernel::IKernelContext& ctx) : Contexted(ctx) { }
|
||||
|
||||
uint64_t getCurrentTime() const override { return 0; }
|
||||
uint64_t getCurrentLateness() const override { return 0; }
|
||||
double getCurrentCPUUsage() const override { return 0; }
|
||||
double getCurrentFastForwardMaximumFactor() const override { return 0; }
|
||||
|
||||
bool stop() override { return true; }
|
||||
bool pause() override { return true; }
|
||||
bool play() override { return true; }
|
||||
bool forward() override { return true; }
|
||||
|
||||
Kernel::EPlayerStatus getStatus() const override { return Kernel::EPlayerStatus::Stop; }
|
||||
Kernel::IConfigurationManager& getConfigurationManager() const override { return m_kernelCtx.getConfigurationManager(); }
|
||||
Kernel::IAlgorithmManager& getAlgorithmManager() const override { return m_kernelCtx.getAlgorithmManager(); }
|
||||
Kernel::ILogManager& getLogManager() const override { return m_kernelCtx.getLogManager(); }
|
||||
Kernel::IErrorManager& getErrorManager() const override { return m_kernelCtx.getErrorManager(); }
|
||||
Kernel::IScenarioManager& getScenarioManager() const override { return m_kernelCtx.getScenarioManager(); }
|
||||
Kernel::ITypeManager& getTypeManager() const override { return m_kernelCtx.getTypeManager(); }
|
||||
|
||||
bool canCreatePluginObject(const CIdentifier& /*pluginID*/) const override { return false; }
|
||||
Plugins::IPluginObject* createPluginObject(const CIdentifier& /*pluginID*/) const override { return nullptr; }
|
||||
bool releasePluginObject(Plugins::IPluginObject* /*pluginObject*/) const override { return true; }
|
||||
|
||||
CIdentifier getClassIdentifier() const override { return CIdentifier(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class TrackerBox
|
||||
* \brief Implements Kernel::IBox
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TrackerBox : protected Contexted, public Kernel::IBox
|
||||
{
|
||||
public:
|
||||
|
||||
explicit TrackerBox(const Kernel::IKernelContext& ctx) : Contexted(ctx) { }
|
||||
CIdentifier getIdentifier() const override { return m_ID; }
|
||||
CString getName() const override { return m_Name; }
|
||||
|
||||
/*
|
||||
CIdentifier getAlgorithmClassIdentifier() override { return m_AlgorithmClassID; };
|
||||
*/
|
||||
|
||||
// @f
|
||||
CIdentifier getUnusedSettingIdentifier(const CIdentifier& id = CIdentifier::undefined()) const override { return id; }
|
||||
CIdentifier getUnusedInputIdentifier(const CIdentifier& id = CIdentifier::undefined()) const override { return id; }
|
||||
CIdentifier getUnusedOutputIdentifier(const CIdentifier& id = CIdentifier::undefined()) const override { return id; }
|
||||
|
||||
bool addInterfacor(const Kernel::EBoxInterfacorType /*type*/, const CString& /*name*/, const CIdentifier& /*id1*/,
|
||||
const CIdentifier& /*id2*/, const bool /*notify*/ = true) override { return false; }
|
||||
|
||||
bool removeInterfacor(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, const bool /*notify*/ = true) override { return false; }
|
||||
|
||||
size_t getInterfacorCount(const Kernel::EBoxInterfacorType interfacor) const override
|
||||
{
|
||||
if (interfacor == Kernel::Setting) { return getSettingCount(); }
|
||||
if (interfacor == Kernel::Input) { return getInputCount(); }
|
||||
if (interfacor == Kernel::Output) { return getOutputCount(); }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t getInterfacorCountIncludingDeprecated(const Kernel::EBoxInterfacorType type) const override { return getInterfacorCount(type); }
|
||||
|
||||
bool getInterfacorIdentifier(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, CIdentifier& /*id*/) const override { return false; }
|
||||
|
||||
bool getInterfacorIndex(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id*/, size_t& /*idx*/) const override { return false; }
|
||||
|
||||
bool getInterfacorIndex(const Kernel::EBoxInterfacorType /*type*/, const CString& /*name*/, size_t& /*idx*/) const override { return false; }
|
||||
|
||||
bool getInterfacorType(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, CIdentifier& /*id*/) const override { return false; }
|
||||
|
||||
bool getInterfacorType(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id1*/,
|
||||
CIdentifier& /*id2*/) const override { return false; }
|
||||
|
||||
bool getInterfacorType(const Kernel::EBoxInterfacorType /*type*/, const CString& /*name*/, CIdentifier& /*id*/) const
|
||||
override { return false; }
|
||||
|
||||
bool getInterfacorName(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, CString& /*name*/) const override { return false; }
|
||||
|
||||
bool getInterfacorName(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id*/, CString& /*name*/) const
|
||||
override { return false; }
|
||||
|
||||
bool getInterfacorDeprecatedStatus(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, bool& /*value*/) const override { return false; }
|
||||
|
||||
bool getInterfacorDeprecatedStatus(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id*/, bool& /*value*/) const
|
||||
override { return false; }
|
||||
|
||||
bool hasInterfacorWithIdentifier(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id*/) const override { return false; }
|
||||
|
||||
bool hasInterfacorWithType(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, const CIdentifier& /*id*/) const
|
||||
override { return false; }
|
||||
|
||||
bool hasInterfacorWithNameAndType(const Kernel::EBoxInterfacorType /*type*/, const CString& /*name*/,
|
||||
const CIdentifier& /*id*/) const override { return false; }
|
||||
|
||||
bool setInterfacorType(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, const CIdentifier& /*id*/) override { return false; }
|
||||
|
||||
bool setInterfacorType(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id1*/,
|
||||
const CIdentifier& /*id2*/) override { return false; }
|
||||
|
||||
bool setInterfacorType(const Kernel::EBoxInterfacorType /*type*/, const CString& /*name*/, const CIdentifier& /*id*/)
|
||||
override { return false; }
|
||||
|
||||
bool setInterfacorName(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, const CString& /*name*/) override { return false; }
|
||||
|
||||
bool setInterfacorName(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id*/, const CString& /*name*/)
|
||||
override { return false; }
|
||||
|
||||
bool setInterfacorDeprecatedStatus(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, const bool /*value*/) override { return false; }
|
||||
|
||||
bool setInterfacorDeprecatedStatus(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id*/, const bool /*value*/)
|
||||
override { return false; }
|
||||
|
||||
bool updateInterfacorIdentifier(const Kernel::EBoxInterfacorType /*type*/, const size_t /*idx*/, const CIdentifier& /*newID*/)
|
||||
override { return false; }
|
||||
|
||||
bool addInterfacorTypeSupport(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id*/) override { return false; }
|
||||
bool hasInterfacorTypeSupport(const Kernel::EBoxInterfacorType /*type*/, const CIdentifier& /*id*/) const override { return false; }
|
||||
|
||||
bool setIdentifier(const CIdentifier& id) override
|
||||
{
|
||||
m_ID = id;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setName(const CString& name) override
|
||||
{
|
||||
m_Name = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setAlgorithmClassIdentifier(const CIdentifier& algorithmClassID) override
|
||||
{
|
||||
m_AlgorithmClassID = algorithmClassID;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool initializeFromAlgorithmClassIdentifier(const CIdentifier& /*algorithmClassID*/) override { return false; }
|
||||
|
||||
bool initializeFromExistingBox(const IBox& existingBox) override
|
||||
{
|
||||
// this->clearBox();
|
||||
this->setName(existingBox.getName());
|
||||
this->setAlgorithmClassIdentifier(existingBox.getAlgorithmClassIdentifier());
|
||||
|
||||
for (size_t i = 0; i < existingBox.getInputCount(); ++i)
|
||||
{
|
||||
CIdentifier typeID = CIdentifier::undefined();
|
||||
CString name;
|
||||
existingBox.getInputType(i, typeID);
|
||||
existingBox.getInputName(i, name);
|
||||
addInput(name, typeID);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < existingBox.getOutputCount(); ++i)
|
||||
{
|
||||
CIdentifier typeID = CIdentifier::undefined();
|
||||
CString name;
|
||||
existingBox.getOutputType(i, typeID);
|
||||
existingBox.getOutputName(i, name);
|
||||
addOutput(name, typeID);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < existingBox.getSettingCount(); ++i)
|
||||
{
|
||||
CIdentifier typeID = CIdentifier::undefined();
|
||||
CString name;
|
||||
CString value;
|
||||
CString defaultValue;
|
||||
bool modifiability = false;
|
||||
const bool notify = false;
|
||||
existingBox.getSettingType(i, typeID);
|
||||
existingBox.getSettingName(i, name);
|
||||
existingBox.getSettingValue(i, value);
|
||||
existingBox.getSettingDefaultValue(i, defaultValue);
|
||||
existingBox.getSettingMod(i, modifiability);
|
||||
addSetting(name, typeID, defaultValue, -1, modifiability);
|
||||
setSettingValue(i, value, notify);
|
||||
}
|
||||
|
||||
CIdentifier id = existingBox.getNextAttributeIdentifier(CIdentifier::undefined());
|
||||
while (id != CIdentifier::undefined())
|
||||
{
|
||||
this->addAttribute(id, existingBox.getAttributeValue(id));
|
||||
id = existingBox.getNextAttributeIdentifier(id);
|
||||
}
|
||||
|
||||
CIdentifier streamTypeID = CIdentifier::undefined();
|
||||
while ((streamTypeID = this->getKernelContext().getTypeManager().getNextTypeIdentifier(streamTypeID)) != CIdentifier::undefined())
|
||||
{
|
||||
if (this->getKernelContext().getTypeManager().isStream(streamTypeID))
|
||||
{
|
||||
//First check if it is a stream
|
||||
if (existingBox.hasInputSupport(streamTypeID)) { this->addInputSupport(streamTypeID); }
|
||||
if (existingBox.hasOutputSupport(streamTypeID)) { this->addOutputSupport(streamTypeID); }
|
||||
}
|
||||
}
|
||||
// this->enableNotification();
|
||||
// this->notify(EBoxModification::Initialized);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addInput(const CString& name, const CIdentifier& typeID, const CIdentifier& /*id*/ = CIdentifier::undefined(), const bool /*notify*/ = true) override
|
||||
{
|
||||
IOEntry tmp;
|
||||
tmp.name = name;
|
||||
tmp.id = typeID;
|
||||
m_Inputs.push_back(tmp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool removeInput(const size_t /*idx*/, const bool /*notify*/ = true) override { return false; }
|
||||
|
||||
size_t getInputCount() const override { return m_Inputs.size(); }
|
||||
|
||||
bool getInputType(const size_t index, CIdentifier& typeID) const override
|
||||
{
|
||||
typeID = m_Inputs[index].id;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getInputName(const size_t index, CString& name) const override
|
||||
{
|
||||
name = m_Inputs[index].name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setInputType(const size_t index, const CIdentifier& typeID) override
|
||||
{
|
||||
if (m_Inputs.size() <= index) { m_Inputs.resize(index + 1); }
|
||||
m_Inputs[index].id = typeID;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setInputName(const size_t index, const CString& name) override
|
||||
{
|
||||
if (m_Inputs.size() <= index) { m_Inputs.resize(index + 1); }
|
||||
m_Inputs[index].name = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool addOutput(const CString& name, const CIdentifier& typeID, const CIdentifier& /*id*/ = CIdentifier::undefined(), const bool /*notify*/ = true) override
|
||||
{
|
||||
IOEntry tmp;
|
||||
tmp.name = name;
|
||||
tmp.id = typeID;
|
||||
m_Outputs.push_back(tmp);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool removeOutput(const size_t /*index*/, const bool /*notify*/ = true) override { return false; }
|
||||
|
||||
size_t getOutputCount() const override { return m_Outputs.size(); }
|
||||
|
||||
bool getOutputType(const size_t index, CIdentifier& typeID) const override
|
||||
{
|
||||
typeID = m_Outputs[index].id;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getOutputName(const size_t index, CString& name) const override
|
||||
{
|
||||
name = m_Outputs[index].name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setOutputType(const size_t index, const CIdentifier& typeID) override
|
||||
{
|
||||
if (m_Outputs.size() <= index) { m_Outputs.resize(index + 1); }
|
||||
m_Outputs[index].id = typeID;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setOutputName(const size_t index, const CString& name) override
|
||||
{
|
||||
if (m_Outputs.size() <= index) { m_Outputs.resize(index + 1); }
|
||||
m_Outputs[index].name = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addSetting(const CString& name, const CIdentifier& typeID, const CString& defaultValue,
|
||||
const size_t index = size_t(-1), const bool /*modiafiability*/ = false,
|
||||
const CIdentifier& /*identifier*/ = CIdentifier::undefined(), const bool /*notify*/ = true) override
|
||||
{
|
||||
Kernel::ITypeManager& typeManager = m_kernelCtx.getTypeManager();
|
||||
|
||||
CString value(defaultValue);
|
||||
if (typeManager.isEnumeration(typeID))
|
||||
{
|
||||
if (typeManager.getEnumerationEntryValueFromName(typeID, defaultValue) == CIdentifier::undefined().id())
|
||||
{
|
||||
if (typeManager.getEnumerationEntryCount(typeID) != 0)
|
||||
{
|
||||
// get value to the first enum entry
|
||||
// and eventually correct this after
|
||||
uint64_t tmp = 0;
|
||||
typeManager.getEnumerationEntry(typeID, 0, value, tmp);
|
||||
|
||||
// Find if the default value string actually is an identifier, otherwise just keep the zero index name as default.
|
||||
CIdentifier id = CIdentifier::undefined();
|
||||
id.fromString(defaultValue);
|
||||
|
||||
// Finally, if it is an identifier, then a name should be found
|
||||
// from the type manager ! Otherwise value is left to the default.
|
||||
const CString candidateValue = typeManager.getEnumerationEntryNameFromValue(typeID, id.id());
|
||||
if (candidateValue != CString("")) { value = candidateValue; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Setting tmp;
|
||||
tmp.name = name;
|
||||
tmp.id = typeID;
|
||||
tmp.value = value;
|
||||
tmp.defaultValue = value;
|
||||
|
||||
const size_t idx = index;
|
||||
|
||||
if (index == size_t(-1) || index == m_Settings.size()) { m_Settings.push_back(tmp); }
|
||||
else
|
||||
{
|
||||
auto it = m_Settings.begin();
|
||||
it += idx;
|
||||
m_Settings.insert(it, tmp);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool removeSetting(const size_t index, const bool /*notify*/ = true) override
|
||||
{
|
||||
auto it = m_Settings.begin() + index;
|
||||
if (it == m_Settings.end())
|
||||
{
|
||||
getLogManager() << Kernel::LogLevel_Error << "Error: No setting found\n";
|
||||
return false;
|
||||
}
|
||||
it = m_Settings.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t getSettingCount() const override { return m_Settings.size(); }
|
||||
|
||||
bool hasSettingWithName(const CString& /*name*/) const override { return false; }
|
||||
|
||||
//virtual int getSettingIndex(const CString& name) const override { return -1; }
|
||||
|
||||
bool getSettingType(const size_t index, CIdentifier& typeID) const override
|
||||
{
|
||||
typeID = m_Settings[index].id;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getSettingName(const size_t index, CString& name) const override
|
||||
{
|
||||
name = m_Settings[index].name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getSettingDefaultValue(const CIdentifier& /*identifier*/, CString& /*rDefaultValue*/) const override { return false; }
|
||||
|
||||
bool getSettingDefaultValue(const size_t index, CString& rDefaultValue) const override
|
||||
{
|
||||
rDefaultValue = m_Settings[index].defaultValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getSettingDefaultValue(const CString& /*name*/, CString& /*rDefaultValue*/) const override { return false; }
|
||||
|
||||
bool getSettingValue(const size_t index, CString& value) const override
|
||||
{
|
||||
if (m_Settings.size() < index) { return false; }
|
||||
|
||||
value = m_Settings[index].value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setSettingType(const size_t index, const CIdentifier& typeID) override
|
||||
{
|
||||
if (m_Settings.size() <= index) { m_Settings.resize(index + 1); }
|
||||
m_Settings[index].id = typeID;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setSettingName(const size_t index, const CString& name) override
|
||||
{
|
||||
if (m_Settings.size() <= index) { m_Settings.resize(index + 1); }
|
||||
m_Settings[index].name = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setSettingDefaultValue(const size_t index, const CString& rDefaultValue) override
|
||||
{
|
||||
if (m_Settings.size() <= index) { m_Settings.resize(index + 1); }
|
||||
m_Settings[index].defaultValue = rDefaultValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setSettingValue(const size_t index, const CString& rValue, const bool /*notify*/ = true) override
|
||||
{
|
||||
if (m_Settings.size() <= index) { m_Settings.resize(index + 1); }
|
||||
m_Settings[index].value = rValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getSettingMod(const size_t /*index*/, bool& /*value*/) const override { return false; }
|
||||
bool setSettingMod(const size_t /*index*/, const bool /*value*/) override { return false; }
|
||||
bool getSettingValue(const CIdentifier& /*identifier*/, CString& /*value*/) const override { return false; }
|
||||
bool getSettingValue(const CString& /*name*/, CString& /*value*/) const override { return false; }
|
||||
bool setSettingDefaultValue(const CIdentifier& /*identifier*/, const CString& /*rDefaultValue*/) override { return false; }
|
||||
bool setSettingDefaultValue(const CString& /*name*/, const CString& /*rDefaultValue*/) override { return false; }
|
||||
bool setSettingValue(const CIdentifier& /*identifier*/, const CString& /*value*/) override { return false; }
|
||||
bool setSettingValue(const CString& /*name*/, const CString& /*value*/) override { return false; }
|
||||
bool getSettingMod(const CIdentifier& /*identifier*/, bool& /*value*/) const override { return false; }
|
||||
bool getSettingMod(const CString& /*name*/, bool& /*value*/) const override { return false; }
|
||||
bool setSettingMod(const CIdentifier& /*identifier*/, const bool /*value*/) override { return false; }
|
||||
bool setSettingMod(const CString& /*name*/, const bool /*value*/) override { return false; }
|
||||
bool swapSettings(const size_t /*indexA*/, const size_t /*indexB*/) override { return false; }
|
||||
bool swapInputs(const size_t /*indexA*/, const size_t /*indexB*/) override { return false; }
|
||||
bool swapOutputs(const size_t /*indexA*/, const size_t /*indexB*/) override { return false; }
|
||||
bool hasModifiableSettings() const override { return false; }
|
||||
|
||||
std::vector<CIdentifier> getInputSupportTypes() const override
|
||||
{
|
||||
std::vector<CIdentifier> tmp;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
std::vector<CIdentifier> getOutputSupportTypes() const override
|
||||
{
|
||||
std::vector<CIdentifier> tmp;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
size_t* getModifiableSettings(size_t& rCount) const override
|
||||
{
|
||||
rCount = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool addInputSupport(const CIdentifier& typeID) override
|
||||
{
|
||||
m_InputSupports.push_back(typeID);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hasInputSupport(const CIdentifier& typeID) const override
|
||||
{
|
||||
return (m_InputSupports.empty() || std::find(m_InputSupports.begin(), m_InputSupports.end(), typeID) != m_InputSupports.end());
|
||||
}
|
||||
|
||||
bool addOutputSupport(const CIdentifier& typeID) override
|
||||
{
|
||||
m_OutputSupports.push_back(typeID);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hasOutputSupport(const CIdentifier& typeID) const override
|
||||
{
|
||||
return (m_OutputSupports.empty() || std::find(m_OutputSupports.begin(), m_OutputSupports.end(), typeID) != m_OutputSupports.end());
|
||||
}
|
||||
|
||||
bool setSupportTypeFromAlgorithmIdentifier(const CIdentifier& /*typeID*/) override { return true; }
|
||||
|
||||
CIdentifier getClassIdentifier() const override { return CIdentifier(); }
|
||||
|
||||
_IsDerivedFromClass_(Kernel::IAttributable, OV_ClassId_Kernel_Scenario_Box)
|
||||
|
||||
struct Setting
|
||||
{
|
||||
CIdentifier id = CIdentifier::undefined();
|
||||
CString name;
|
||||
CString value;
|
||||
CString defaultValue;
|
||||
};
|
||||
|
||||
struct IOEntry
|
||||
{
|
||||
CIdentifier id = CIdentifier::undefined();
|
||||
CString name;
|
||||
};
|
||||
|
||||
std::vector<Setting> m_Settings;
|
||||
std::vector<IOEntry> m_Inputs;
|
||||
std::vector<IOEntry> m_Outputs;
|
||||
std::vector<CIdentifier> m_InputSupports;
|
||||
std::vector<CIdentifier> m_OutputSupports;
|
||||
|
||||
// This box
|
||||
CString m_Name;
|
||||
CIdentifier m_ID = CIdentifier::undefined();
|
||||
CIdentifier m_AlgorithmClassID = CIdentifier::undefined();
|
||||
|
||||
// Attributable
|
||||
bool addAttribute(const CIdentifier& /*attributeID*/, const CString& /*sAttributeValue*/) override { return true; }
|
||||
bool removeAttribute(const CIdentifier& /*attributeID*/) override { return true; }
|
||||
bool removeAllAttributes() override { return true; }
|
||||
CString getAttributeValue(const CIdentifier& /*attributeID*/) const override { return CString(""); }
|
||||
bool setAttributeValue(const CIdentifier& /*attributeID*/, const CString& /*sAttributeValue*/) override { return true; }
|
||||
bool hasAttribute(const CIdentifier& /*attributeID*/) const override { return false; }
|
||||
bool hasAttributes() const override { return false; }
|
||||
|
||||
CIdentifier getNextAttributeIdentifier(const CIdentifier& /*previousID*/) const override { return CIdentifier(); }
|
||||
|
||||
// @f
|
||||
void clearOutputSupportTypes() override { }
|
||||
void clearInputSupportTypes() override { }
|
||||
|
||||
CIdentifier getAlgorithmClassIdentifier() const override { return CIdentifier(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class TrackerBoxProto
|
||||
* \brief Implements Kernel::IBoxProto
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TrackerBoxProto : public TrackerBox, public Kernel::IBoxProto
|
||||
{
|
||||
public:
|
||||
explicit TrackerBoxProto(const Kernel::IKernelContext& ctx) : TrackerBox(ctx) {}
|
||||
|
||||
virtual bool addSetting(const CString& name, const CIdentifier& typeID, const CString& defaultValue,
|
||||
const bool modifiable = false) { return TrackerBox::addSetting(name, typeID, defaultValue, -1, modifiable); }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \class TrackerBoxIO
|
||||
* \brief Implements Kernel::IBoxIO
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TrackerBoxIO final : public Kernel::IBoxIO
|
||||
{
|
||||
public:
|
||||
|
||||
struct SBufferWithStamps
|
||||
{
|
||||
CMemoryBuffer* buffer;
|
||||
CTime startTime;
|
||||
CTime endTime;
|
||||
};
|
||||
|
||||
std::vector<std::vector<SBufferWithStamps>> m_InputChunks; // Queue per input. See code for comments.
|
||||
std::vector<IMemoryBuffer*> m_OutputChunks; // Just one buffer per output
|
||||
std::vector<size_t> m_Ready;
|
||||
std::vector<CTime> m_StartTime;
|
||||
std::vector<CTime> m_EndTime;
|
||||
|
||||
~TrackerBoxIO() override { for (size_t i = 0; i < m_OutputChunks.size(); ++i) { delete m_OutputChunks[i]; } }
|
||||
|
||||
bool initialize(const Kernel::IBox* boxCtx)
|
||||
{
|
||||
for (size_t i = 0; i < m_OutputChunks.size(); ++i) { delete m_OutputChunks[i]; }
|
||||
|
||||
m_InputChunks.resize(boxCtx->getInputCount());
|
||||
m_OutputChunks.resize(boxCtx->getOutputCount());
|
||||
m_Ready.resize(boxCtx->getOutputCount(), 0);
|
||||
m_StartTime.resize(boxCtx->getOutputCount());
|
||||
m_EndTime.resize(boxCtx->getOutputCount());
|
||||
|
||||
for (size_t i = 0; i < m_OutputChunks.size(); ++i) { m_OutputChunks[i] = new CMemoryBuffer(); }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t getInputChunkCount(const size_t index) const override
|
||||
{
|
||||
if (index >= m_InputChunks.size()) { return 0; }
|
||||
return m_InputChunks[index].size();
|
||||
}
|
||||
|
||||
bool getInputChunk(const size_t index, const size_t chunkIdx, uint64_t& startTime, uint64_t& endTime, size_t& size,
|
||||
const uint8_t*& buffer) const override
|
||||
{
|
||||
const std::vector<SBufferWithStamps>& chunks = m_InputChunks[index];
|
||||
const SBufferWithStamps& chk = chunks[chunkIdx];
|
||||
|
||||
startTime = chk.startTime.time();
|
||||
endTime = chk.endTime.time();
|
||||
size = chk.buffer->getSize();
|
||||
buffer = chk.buffer->getDirectPointer();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Essentially this function being const and requiring its output as IMemoryBuffer prevents us from simply carrying our data as std::vector<EncodedChunk>.
|
||||
const IMemoryBuffer* getInputChunk(const size_t index, const size_t chunkIdx) const override
|
||||
{
|
||||
const std::vector<SBufferWithStamps>& chunks = m_InputChunks[index];
|
||||
const SBufferWithStamps& chk = chunks[chunkIdx];
|
||||
|
||||
return chk.buffer;
|
||||
}
|
||||
|
||||
uint64_t getInputChunkStartTime(const size_t index, const size_t chunkIdx) const override
|
||||
{
|
||||
const std::vector<SBufferWithStamps>& chunks = m_InputChunks[index];
|
||||
const SBufferWithStamps& chk = chunks[chunkIdx];
|
||||
|
||||
return chk.startTime.time();
|
||||
}
|
||||
|
||||
uint64_t getInputChunkEndTime(const size_t index, const size_t chunkIdx) const override
|
||||
{
|
||||
const std::vector<SBufferWithStamps>& chunks = m_InputChunks[index];
|
||||
const SBufferWithStamps& chk = chunks[chunkIdx];
|
||||
|
||||
return chk.endTime.time();
|
||||
}
|
||||
|
||||
bool markInputAsDeprecated(const size_t /*index*/, const size_t /*chunkIdx*/) override { return true; }
|
||||
|
||||
size_t getOutputChunkSize(const size_t index) const override
|
||||
{
|
||||
if (index >= m_OutputChunks.size()) { return false; }
|
||||
return m_OutputChunks[index]->getSize();
|
||||
}
|
||||
|
||||
bool setOutputChunkSize(const size_t index, const size_t size, const bool discard = true) override
|
||||
{
|
||||
if (m_OutputChunks.size() <= index) { m_OutputChunks.resize(index + 1); }
|
||||
m_OutputChunks[index]->setSize(size, discard);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t* getOutputChunkBuffer(const size_t index) override
|
||||
{
|
||||
if (m_OutputChunks.size() >= index) { return nullptr; }
|
||||
return m_OutputChunks[index]->getDirectPointer();
|
||||
}
|
||||
|
||||
bool appendOutputChunkData(const size_t index, const uint8_t* buffer, const size_t size) override { return m_OutputChunks[index]->append(buffer, size); }
|
||||
|
||||
IMemoryBuffer* getOutputChunk(const size_t index) override { return m_OutputChunks[index]; }
|
||||
|
||||
bool getOutputChunk(const size_t index, EncodedChunk& chk)
|
||||
{
|
||||
chk.m_Buffer.assign(m_OutputChunks[index]->getDirectPointer(),
|
||||
m_OutputChunks[index]->getDirectPointer() + m_OutputChunks[index]->getSize());
|
||||
chk.m_StartTime = m_StartTime[index];
|
||||
chk.m_EndTime = m_EndTime[index];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool markOutputAsReadyToSend(const size_t index, const uint64_t startTime, const uint64_t endTime) override
|
||||
{
|
||||
m_StartTime[index] = startTime;
|
||||
m_EndTime[index] = endTime;
|
||||
m_Ready[index] = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
CIdentifier getClassIdentifier() const override { return CIdentifier(); }
|
||||
|
||||
bool addInputChunk(const size_t index, const EncodedChunk& chk)
|
||||
{
|
||||
if (m_InputChunks.size() <= index) { m_InputChunks.resize(index + 1); }
|
||||
SBufferWithStamps buf;
|
||||
buf.startTime = chk.m_StartTime;
|
||||
buf.endTime = chk.m_EndTime;
|
||||
// We cannot copy CMemoryBuffer, so have to use new() @fixme SDK should implement working copy or prevent
|
||||
buf.buffer = new CMemoryBuffer(&chk.m_Buffer[0], chk.m_Buffer.size());
|
||||
m_InputChunks[index].push_back(buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool clearInputChunks()
|
||||
{
|
||||
for (size_t i = 0; i < m_InputChunks.size(); ++i)
|
||||
{
|
||||
for (auto& chk : m_InputChunks[i]) { delete chk.buffer; }
|
||||
m_InputChunks[i].clear();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isReadyToSend(const size_t outputIdx) { return (m_Ready[outputIdx] != 0); }
|
||||
|
||||
bool deprecateOutput(const size_t outputIdx)
|
||||
{
|
||||
m_OutputChunks[outputIdx]->setSize(0, true);
|
||||
m_Ready[outputIdx] = false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \class TrackerBoxAlgorithmContext
|
||||
* \brief Implements Kernel::IBoxAlgorithmContext
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TrackerBoxAlgorithmContext final : protected Contexted, public Kernel::IBoxAlgorithmContext
|
||||
{
|
||||
public:
|
||||
|
||||
explicit TrackerBoxAlgorithmContext(const Kernel::IKernelContext& ctx) : Contexted(ctx), m_StaticBoxCtx(ctx), m_PlayerCtx(ctx) { }
|
||||
const Kernel::IBox* getStaticBoxContext() override { return &m_StaticBoxCtx; }
|
||||
Kernel::IBoxIO* getDynamicBoxContext() override { return &m_DynamicBoxCtx; }
|
||||
Kernel::IPlayerContext* getPlayerContext() override { return &m_PlayerCtx; }
|
||||
bool markAlgorithmAsReadyToProcess() override { return true; }
|
||||
CIdentifier getClassIdentifier() const override { return CIdentifier(); }
|
||||
TrackerBoxIO* getTrackerBoxIO() { return &m_DynamicBoxCtx; }
|
||||
|
||||
TrackerBox m_StaticBoxCtx;
|
||||
TrackerBoxIO m_DynamicBoxCtx;
|
||||
TrackerPlayerContext m_PlayerCtx;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
//
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
#include "Contexted.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Kernel {
|
||||
|
||||
/**
|
||||
* \class IBoxIOProxy
|
||||
* \brief This proxy is intended for reusing codecs from the toolkit.
|
||||
* @fixme it'd be more elegant not to duplicate this interface as a very similar one is already implemented in BoxAdapterHelper.
|
||||
* however unifying the two would need some work.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class OV_API_Export IBoxIOProxy final : public IBoxIO
|
||||
{
|
||||
public:
|
||||
size_t getInputChunkCount(const size_t /*index*/) const override { return 0; }
|
||||
|
||||
bool getInputChunk(const size_t /*inputIdx*/, const size_t /*chunkIdx*/, uint64_t& /*startTime*/, uint64_t& /*endTime*/, size_t& /*chunkSize*/,
|
||||
const uint8_t*& /*chunkBuffer*/) const override { return true; }
|
||||
|
||||
const IMemoryBuffer* getInputChunk(const size_t /*inputIdx*/, const size_t /*chunkIdx*/) const override { return &m_InBuffer; }
|
||||
uint64_t getInputChunkStartTime(const size_t /*inputIdx*/, const size_t /*chunkIdx*/) const override { return 0; }
|
||||
uint64_t getInputChunkEndTime(const size_t /*inputIdx*/, const size_t /*chunkIdx*/) const override { return 0; }
|
||||
bool markInputAsDeprecated(const size_t /*inputIdx*/, const size_t /*chunkIdx*/) override { return true; }
|
||||
size_t getOutputChunkSize(const size_t /*index*/) const override { return m_OutBuffer.getSize(); }
|
||||
|
||||
bool setOutputChunkSize(const size_t /*index*/, const size_t size, const bool /*discard*/ = true) override { return m_OutBuffer.setSize(size, true); }
|
||||
|
||||
uint8_t* getOutputChunkBuffer(const size_t /*index*/) override { return m_OutBuffer.getDirectPointer(); }
|
||||
bool appendOutputChunkData(const size_t /*index*/, const uint8_t* /*buffer*/, const size_t /*size*/) override { return false; } // not implemented
|
||||
|
||||
IMemoryBuffer* getOutputChunk(const size_t /*outputIdx*/) override { return &m_OutBuffer; }
|
||||
|
||||
bool markOutputAsReadyToSend(const size_t /*outputIdx*/, const uint64_t /*startTime*/, const uint64_t /*endTime*/) override { return true; }
|
||||
|
||||
CIdentifier getClassIdentifier() const override { return 0; }
|
||||
|
||||
CMemoryBuffer m_InBuffer;
|
||||
CMemoryBuffer m_OutBuffer;
|
||||
};
|
||||
} // namespace Kernel
|
||||
} // namespace OpenViBE
|
||||
|
||||
/**
|
||||
* \class BoxAlgorithmProxy
|
||||
* \brief This proxy is needed in order to use the stream codecs from the toolkit
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class BoxAlgorithmProxy final : protected OpenViBE::Tracker::Contexted
|
||||
{
|
||||
public:
|
||||
explicit BoxAlgorithmProxy(const OpenViBE::Kernel::IKernelContext& ctx) : Contexted(ctx) { }
|
||||
|
||||
OpenViBE::Kernel::IBoxIO& getDynamicBoxContext() { return dummy; }
|
||||
static bool markAlgorithmAsReadyToProcess() { return true; }
|
||||
|
||||
OpenViBE::Kernel::IAlgorithmManager& getAlgorithmManager() const override { return Contexted::getAlgorithmManager(); }
|
||||
|
||||
OpenViBE::Kernel::IBoxIOProxy dummy;
|
||||
};
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "BoxAdapter.h"
|
||||
#include "Contexted.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
/**
|
||||
* \class BoxPlugins
|
||||
* \brief Box Plugins is a factory-like class that keeps a list of box plugins that can be 'applied' to streams in Tracker
|
||||
* \details Boxes can be registered in the constructor
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class BoxPlugins final : protected Contexted
|
||||
{
|
||||
public:
|
||||
|
||||
explicit BoxPlugins(const Kernel::IKernelContext& ctx);
|
||||
~BoxPlugins() override { for (auto ptr : m_boxPlugins) { delete ptr; } }
|
||||
|
||||
// Get a list of available box plugins
|
||||
const std::vector<BoxAdapterStream*>& getBoxPlugins() const { return m_boxPlugins; }
|
||||
|
||||
protected:
|
||||
|
||||
bool create(const CIdentifier& streamType, const CIdentifier& alg, const CIdentifier& desc);
|
||||
|
||||
std::vector<BoxAdapterStream*> m_boxPlugins;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+153
@@ -0,0 +1,153 @@
|
||||
#pragma once
|
||||
|
||||
#include "ovd_base.h"
|
||||
#include "ovdCLogListenerDesigner.h"
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class CLogListenerTracker
|
||||
* \brief Log listener wrapper for CLogListenerDesigner that can be called from multiple threads
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class CLogListenerTracker final : public Kernel::ILogListener
|
||||
{
|
||||
public:
|
||||
|
||||
CLogListenerTracker(const Kernel::IKernelContext& ctx, GtkBuilder* builder) : m_impl(ctx, builder) { }
|
||||
|
||||
// @note Only call from gtk thread
|
||||
void clearMessages()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
// std::cout << "Intercepted\n";
|
||||
m_impl.clearMessages();
|
||||
}
|
||||
|
||||
bool isActive(const Kernel::ELogLevel level) override
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
return m_impl.isActive(level);
|
||||
}
|
||||
|
||||
bool activate(const Kernel::ELogLevel logLevel, const bool active) override
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
return m_impl.activate(logLevel, active);
|
||||
}
|
||||
|
||||
bool activate(const Kernel::ELogLevel startLogLevel, const Kernel::ELogLevel endLogLevel, const bool active) override
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
return m_impl.activate(startLogLevel, endLogLevel, active);
|
||||
}
|
||||
|
||||
bool activate(const bool active) override
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
return m_impl.activate(active);
|
||||
}
|
||||
|
||||
// This function encapsulates CLogListenerDesigner so that its gtk calls will be run from g_idle_add().
|
||||
template <class T>
|
||||
void logWrapper(T param)
|
||||
{
|
||||
auto ptr = new std::pair<CLogListenerTracker*, T>(this, param);
|
||||
|
||||
auto fun = [](gpointer data)
|
||||
{
|
||||
auto ptr = static_cast<std::pair<CLogListenerTracker*, T>*>(data);
|
||||
|
||||
// LogListenerDesigner may not be thread safe, so we lock here for safety as activate() and isActive() do not go through the g_idle_add().
|
||||
std::unique_lock<std::mutex> lock(ptr->first->getMutex());
|
||||
|
||||
ptr->first->getImpl().log(ptr->second);
|
||||
delete ptr;
|
||||
|
||||
return gboolean(FALSE);
|
||||
};
|
||||
|
||||
g_idle_add(fun, ptr);
|
||||
}
|
||||
|
||||
// Specialization for const char*. It frees memory. We don't call it
|
||||
// template<> logWrapper() to avoid gcc complaining.
|
||||
void logWrapperCharPtr(const char* param)
|
||||
{
|
||||
const auto ptr = new std::pair<CLogListenerTracker*, const char*>(this, param);
|
||||
|
||||
const auto fun = [](gpointer data)
|
||||
{
|
||||
auto ptr = static_cast<std::pair<CLogListenerTracker*, const char*>*>(data);
|
||||
|
||||
// LogListenerDesigner may not be thread safe, so we lock here for safety as activate() and isActive() do not go through the g_idle_add().
|
||||
std::unique_lock<std::mutex> lock(ptr->first->getMutex());
|
||||
|
||||
ptr->first->getImpl().log(ptr->second);
|
||||
delete[] ptr->second;
|
||||
delete ptr;
|
||||
|
||||
return gboolean(FALSE);
|
||||
};
|
||||
|
||||
g_idle_add(fun, ptr);
|
||||
}
|
||||
|
||||
void log(const CTime value) override { logWrapper(value); }
|
||||
void log(const uint64_t value) override { logWrapper(value); }
|
||||
void log(const uint32_t value) override { logWrapper(value); }
|
||||
void log(const int64_t value) override { logWrapper(value); }
|
||||
void log(const int value) override { logWrapper(value); }
|
||||
void log(const double value) override { logWrapper(value); }
|
||||
void log(const bool value) override { logWrapper(value); }
|
||||
void log(const Kernel::ELogLevel eLogLevel) override { logWrapper(eLogLevel); }
|
||||
void log(const Kernel::ELogColor eLogColor) override { logWrapper(eLogColor); }
|
||||
|
||||
// With these calls we need to make copies, as by the time the actual calls are run in gtk_idle, the originals may be gone
|
||||
void log(const CIdentifier& value) override
|
||||
{
|
||||
const CIdentifier nonRef = value;
|
||||
logWrapper(nonRef);
|
||||
}
|
||||
|
||||
void log(const CString& value) override
|
||||
{
|
||||
const CString nonRef = value;
|
||||
logWrapper(nonRef);
|
||||
}
|
||||
|
||||
void log(const std::string& value) override
|
||||
{
|
||||
const std::string nonRef = value;
|
||||
logWrapper(nonRef);
|
||||
}
|
||||
|
||||
void log(const char* value) override
|
||||
{
|
||||
// This one is tricksy, we cannot simply use CString as we'd lose color, but with char* we need to free the memory. Use a specialization.
|
||||
#if defined(TARGET_OS_Windows)
|
||||
const char* valueCopy = _strdup(value);
|
||||
#else
|
||||
const char* valueCopy = strdup(value);
|
||||
#endif
|
||||
logWrapperCharPtr(valueCopy);
|
||||
}
|
||||
|
||||
// Callbacks need access to these
|
||||
Designer::CLogListenerDesigner& getImpl() { return m_impl; }
|
||||
std::mutex& getMutex() { return m_mutex; }
|
||||
|
||||
// getClassIdentifier()
|
||||
_IsDerivedFromClass_Final_(Kernel::ILogListener, CIdentifier::undefined())
|
||||
|
||||
protected:
|
||||
Designer::CLogListenerDesigner m_impl;
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "Workspace.h"
|
||||
#include "BoxPlugins.h"
|
||||
#include "TrackerPlugins.h"
|
||||
#include "Contexted.h"
|
||||
|
||||
#include "ParallelExecutor.h"
|
||||
|
||||
/**
|
||||
* \namespace OpenViBE::Tracker
|
||||
* \author J.T. Lindgren (Inria)
|
||||
* \date 2018-09
|
||||
* \brief Main OpenViBE Tracker namespace
|
||||
*
|
||||
* All the classes defined in the OpenViBE Tracker are currently included in
|
||||
* this namespace.
|
||||
*/
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class CTracker
|
||||
* \author J. T. Lindgren
|
||||
* \brief Tracker is the main class of OpenViBE Tracker that encapsulates the Workspace
|
||||
* \details
|
||||
* The tracker is in one of the states Stopped, Playing, Paused.
|
||||
*
|
||||
* - play() will start sending data to a specified processor from the tracks in the workspace.
|
||||
* - step() will send out the chunk that is next in time order. Usually called by the GUI idle loop.
|
||||
* - stop() will stop sending. Tracker can also stop if the processor quits or if all the data has been sent.
|
||||
*
|
||||
* In the stopped state, the Tracker also allows plugins to be applied to the Workspace content.
|
||||
*
|
||||
* In the future we can extend this class to host more than one workspace at the same time.
|
||||
*
|
||||
*/
|
||||
class CTracker final : protected Contexted
|
||||
{
|
||||
public:
|
||||
explicit CTracker(const Kernel::IKernelContext& ctx);
|
||||
~CTracker() override;
|
||||
|
||||
enum class EStates
|
||||
{
|
||||
Stopped = 0,
|
||||
Playing,
|
||||
Paused,
|
||||
Stopping
|
||||
};
|
||||
|
||||
bool initialize();
|
||||
|
||||
bool play(const bool playFast);
|
||||
bool stop();
|
||||
|
||||
EStates step();
|
||||
|
||||
// Useful getters
|
||||
EStates getCurrentState() const { return m_state; }
|
||||
Workspace& getWorkspace() { return m_workspace; }
|
||||
const Kernel::IKernelContext& getKernelContext() const override { return m_kernelCtx; }
|
||||
|
||||
// Plugin handling
|
||||
const BoxPlugins& getBoxPlugins() const { return m_boxPlugins; }
|
||||
const TrackerPlugins& getTrackerPlugins() const { return m_trackerPlugins; }
|
||||
bool applyBoxPlugin(size_t index);
|
||||
bool applyTrackerPlugin(size_t index);
|
||||
|
||||
uint32_t getNumThreads() const { return m_executor.getNumThreads(); }
|
||||
bool setNumThreads(uint32_t numThreads);
|
||||
|
||||
// Configuration handling
|
||||
bool saveConfig(const CString& filename) const;
|
||||
bool loadConfig(const CString& filename);
|
||||
|
||||
protected:
|
||||
Workspace m_workspace;
|
||||
EStates m_state = EStates::Stopped;
|
||||
BoxPlugins m_boxPlugins;
|
||||
TrackerPlugins m_trackerPlugins;
|
||||
ParallelExecutor m_executor;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class Chunk
|
||||
* \brief Base class for all OpenViBE stream content (chunk) types
|
||||
* \details
|
||||
*
|
||||
* Regardless of the type, all chunks in OpenViBE have start and end timestamps. Hence
|
||||
* they should derive from this class. In particular, Header, Buffer and End chunks
|
||||
* for some particular type should ultimately derive from this class. However,
|
||||
* this class defines no content.
|
||||
*
|
||||
* @todo In the future if these types turn out to be more generally useful, we could consider
|
||||
* moving them to the Kernel.
|
||||
*
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
|
||||
class Chunk
|
||||
{
|
||||
public:
|
||||
CTime m_StartTime = CTime::min();
|
||||
CTime m_EndTime = CTime::min();
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "EncodedChunk.h"
|
||||
#include "Encoder.h"
|
||||
#include "Decoder.h"
|
||||
|
||||
#include "StreamBase.h"
|
||||
#include "Stream.h"
|
||||
|
||||
#include "CodecsAll.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class CodecFactory
|
||||
* \brief A factory class for obtaining encoders and decoders for streams.
|
||||
* \details In OpenViBE, every stream is passed around as EBML encoded due to an old design choice.
|
||||
* The encoders and decoders are used to pass between the plain and encoded formats of the data.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class CodecFactory
|
||||
{
|
||||
public:
|
||||
|
||||
// Factory for encodes
|
||||
static EncoderBase* getEncoder(const Kernel::IKernelContext& ctx, StreamBase& stream)
|
||||
{
|
||||
EncoderBase* encoder;
|
||||
const CIdentifier typeID = stream.getTypeIdentifier();
|
||||
|
||||
if (typeID == OVTK_TypeId_StreamedMatrix) { encoder = new Encoder<TypeMatrix>(ctx, reinterpret_cast<Stream<TypeMatrix>&>(stream)); }
|
||||
else if (typeID == OVTK_TypeId_Signal) { encoder = new Encoder<TypeSignal>(ctx, reinterpret_cast<Stream<TypeSignal>&>(stream)); }
|
||||
else if (typeID == OVTK_TypeId_Stimulations) { encoder = new Encoder<TypeStimulation>(ctx, reinterpret_cast<Stream<TypeStimulation>&>(stream)); }
|
||||
else if (typeID == OVTK_TypeId_ExperimentInfo)
|
||||
{
|
||||
encoder = new Encoder<TypeExperimentInfo>(ctx, reinterpret_cast<Stream<TypeExperimentInfo>&>(stream));
|
||||
}
|
||||
else if (typeID == OV_TypeId_ChannelLocalisation) // bug in toolkit atm with OVTK_ define
|
||||
{
|
||||
encoder = new Encoder<TypeChannelLocalization>(ctx, reinterpret_cast<Stream<TypeChannelLocalization>&>(stream));
|
||||
}
|
||||
else if (typeID == OV_TypeId_ChannelUnits) // bug in toolkit atm with OVTK_ define
|
||||
{
|
||||
encoder = new Encoder<TypeChannelUnits>(ctx, reinterpret_cast<Stream<TypeChannelUnits>&>(stream));
|
||||
}
|
||||
else if (typeID == OVTK_TypeId_Spectrum) { encoder = new Encoder<TypeSpectrum>(ctx, reinterpret_cast<Stream<TypeSpectrum>&>(stream)); }
|
||||
else if (typeID == OVTK_TypeId_FeatureVector) { encoder = new Encoder<TypeFeatureVector>(ctx, reinterpret_cast<Stream<TypeFeatureVector>&>(stream)); }
|
||||
else
|
||||
{
|
||||
ctx.getLogManager() << Kernel::LogLevel_Info << "Warning: Unknown Decoder type " << ctx.getTypeManager().getTypeName(typeID) << " " <<
|
||||
typeID.str() << "\n";
|
||||
encoder = new Encoder<TypeError>(ctx, reinterpret_cast<Stream<TypeError>&>(stream));
|
||||
}
|
||||
|
||||
return encoder;
|
||||
}
|
||||
|
||||
// Factory for decoders
|
||||
static DecoderBase* getDecoder(const Kernel::IKernelContext& ctx, StreamBase& stream)
|
||||
{
|
||||
DecoderBase* decoder;
|
||||
const CIdentifier typeID = stream.getTypeIdentifier();
|
||||
|
||||
if (typeID == OVTK_TypeId_StreamedMatrix) { decoder = new Decoder<TypeMatrix>(ctx, reinterpret_cast<Stream<TypeMatrix>&>(stream)); }
|
||||
else if (typeID == OVTK_TypeId_Signal) { decoder = new Decoder<TypeSignal>(ctx, reinterpret_cast<Stream<TypeSignal>&>(stream)); }
|
||||
else if (typeID == OVTK_TypeId_Stimulations) { decoder = new Decoder<TypeStimulation>(ctx, reinterpret_cast<Stream<TypeStimulation>&>(stream)); }
|
||||
else if (typeID == OVTK_TypeId_ExperimentInfo)
|
||||
{
|
||||
decoder = new Decoder<TypeExperimentInfo>(ctx, reinterpret_cast<Stream<TypeExperimentInfo>&>(stream));
|
||||
}
|
||||
else if (typeID == OV_TypeId_ChannelLocalisation) // bug in toolkit atm with OVTK_ define
|
||||
{
|
||||
decoder = new Decoder<TypeChannelLocalization>(ctx, reinterpret_cast<Stream<TypeChannelLocalization>&>(stream));
|
||||
}
|
||||
else if (typeID == OV_TypeId_ChannelUnits) // bug in toolkit atm with OVTK_ define
|
||||
{
|
||||
decoder = new Decoder<TypeChannelUnits>(ctx, reinterpret_cast<Stream<TypeChannelUnits>&>(stream));
|
||||
}
|
||||
else if (typeID == OVTK_TypeId_Spectrum) { decoder = new Decoder<TypeSpectrum>(ctx, reinterpret_cast<Stream<TypeSpectrum>&>(stream)); }
|
||||
else if (typeID == OVTK_TypeId_FeatureVector) { decoder = new Decoder<TypeFeatureVector>(ctx, reinterpret_cast<Stream<TypeFeatureVector>&>(stream)); }
|
||||
else
|
||||
{
|
||||
ctx.getLogManager() << Kernel::LogLevel_Info << "Warning: Unknown Decoder type " << ctx.getTypeManager().getTypeName(typeID) << " " <<
|
||||
typeID.str() << "\n";
|
||||
decoder = new Decoder<TypeError>(ctx, reinterpret_cast<Stream<TypeError>&>(stream));
|
||||
}
|
||||
|
||||
return decoder;
|
||||
}
|
||||
|
||||
// Prevent constructing
|
||||
CodecFactory() = delete;
|
||||
CodecFactory(const CodecFactory&) = delete;
|
||||
CodecFactory(CodecFactory&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "Encoder.h"
|
||||
#include "Decoder.h"
|
||||
|
||||
#include "TypeError.h"
|
||||
|
||||
// This macro makes specializations by instantiating an adapter that handles a type with
|
||||
// the actual encoder and decoder from the toolkit
|
||||
#define CODEC_IMPL_VIA_TOOLKIT(TYPENAME, TOOLKITENCODER, TOOLKITDECODER) \
|
||||
template<> class DecoderImpl<TYPENAME> : public DecoderAdapter< TYPENAME, OpenViBE::Toolkit::TOOLKITDECODER<BoxAlgorithmProxy> > \
|
||||
{ \
|
||||
public: \
|
||||
DecoderImpl(const OpenViBE::Kernel::IKernelContext& ctx) : DecoderAdapter(ctx) { } \
|
||||
};\
|
||||
template<> class EncoderImpl<TYPENAME> : public EncoderAdapter< TYPENAME, OpenViBE::Toolkit::TOOLKITENCODER<BoxAlgorithmProxy> > \
|
||||
{ \
|
||||
public: \
|
||||
EncoderImpl(const OpenViBE::Kernel::IKernelContext& ctx) : EncoderAdapter(ctx) { } \
|
||||
};
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
/**
|
||||
* \class DecoderImpl
|
||||
* \brief Fallback class for invalid situations
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
template <>
|
||||
class DecoderImpl<TypeError>
|
||||
{
|
||||
public:
|
||||
explicit DecoderImpl(const Kernel::IKernelContext& /*ctx*/) { }
|
||||
virtual ~DecoderImpl() = default;
|
||||
|
||||
virtual bool decode(const EncodedChunk& /*source*/) { return false; }
|
||||
virtual bool getHeader(TypeError::Header& /*h*/) { return false; }
|
||||
virtual bool getBuffer(TypeError::Buffer& /*b*/) { return false; }
|
||||
virtual bool getEnd(TypeError::End& /*e*/) { return false; }
|
||||
virtual bool isHeaderReceived() { return false; }
|
||||
virtual bool isBufferReceived() { return false; }
|
||||
virtual bool isEndReceived() { return false; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class EncoderImpl
|
||||
* \brief Fallback class for invalid situations
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
template <>
|
||||
class EncoderImpl<TypeError>
|
||||
{
|
||||
public:
|
||||
explicit EncoderImpl(const Kernel::IKernelContext& /*ctx*/) { }
|
||||
virtual ~EncoderImpl() = default;
|
||||
|
||||
bool encodeHeader(EncodedChunk& /*chk*/, const TypeError::Header& /*hdr*/) { return false; }
|
||||
bool encodeBuffer(EncodedChunk& /*chk*/, const TypeError::Buffer& /*buf*/) { return false; }
|
||||
bool encodeEnd(EncodedChunk& /*chk*/, const TypeError::End& /*end*/) { return false; }
|
||||
bool setEncodeOffset(CTime /*newOffset*/) { return false; }
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "CodecImpl.h"
|
||||
|
||||
#include "TypeChannelLocalization.h"
|
||||
#include "TypeChannelUnits.h"
|
||||
#include "TypeExperimentInfo.h"
|
||||
#include "TypeFeatureVector.h"
|
||||
#include "TypeMatrix.h"
|
||||
#include "TypeSignal.h"
|
||||
#include "TypeSpectrum.h"
|
||||
#include "TypeStimulation.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
CODEC_IMPL_VIA_TOOLKIT(TypeChannelLocalization, TChannelLocalisationEncoder, TChannelLocalisationDecoder)
|
||||
|
||||
CODEC_IMPL_VIA_TOOLKIT(TypeChannelUnits, TChannelUnitsEncoder, TChannelUnitsDecoder)
|
||||
|
||||
CODEC_IMPL_VIA_TOOLKIT(TypeExperimentInfo, TExperimentInfoEncoder, TExperimentInfoDecoder)
|
||||
|
||||
CODEC_IMPL_VIA_TOOLKIT(TypeFeatureVector, TFeatureVectorEncoder, TFeatureVectorDecoder)
|
||||
|
||||
CODEC_IMPL_VIA_TOOLKIT(TypeMatrix, TStreamedMatrixEncoder, TStreamedMatrixDecoder)
|
||||
|
||||
CODEC_IMPL_VIA_TOOLKIT(TypeSignal, TSignalEncoder, TSignalDecoder)
|
||||
|
||||
CODEC_IMPL_VIA_TOOLKIT(TypeSpectrum, TSpectrumEncoder, TSpectrumDecoder)
|
||||
|
||||
CODEC_IMPL_VIA_TOOLKIT(TypeStimulation, TStimulationEncoder, TStimulationDecoder)
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class Contexted
|
||||
* \brief This class can be used to provide KernelContext and related getters for derived classes without overly complicating the derived class interface in their headers.
|
||||
* \author J. T. Lindgren
|
||||
* @fixme actually this class does pretty much the same thing as Kernel::TKernelObject
|
||||
*
|
||||
*/
|
||||
class Contexted
|
||||
{
|
||||
public:
|
||||
explicit Contexted(const Kernel::IKernelContext& ctx) : m_kernelCtx(ctx) { }
|
||||
virtual ~Contexted() { }
|
||||
|
||||
virtual const Kernel::IKernelContext& getKernelContext() const { return m_kernelCtx; }
|
||||
|
||||
virtual Kernel::IAlgorithmManager& getAlgorithmManager() const { return m_kernelCtx.getAlgorithmManager(); }
|
||||
virtual Kernel::IErrorManager& getErrorManager() const { return m_kernelCtx.getErrorManager(); }
|
||||
virtual Kernel::ILogManager& getLogManager() const { return m_kernelCtx.getLogManager(); }
|
||||
virtual Kernel::ITypeManager& getTypeManager() const { return m_kernelCtx.getTypeManager(); }
|
||||
|
||||
// Convenience handle to getLogManager()
|
||||
virtual Kernel::ILogManager& log() const { return getLogManager(); }
|
||||
|
||||
Contexted() = delete;
|
||||
|
||||
// Needed if Contexted is used as a base class for TAttributable
|
||||
// virtual bool isDerivedFromClass(const CIdentifier& classID) const { return false; }
|
||||
|
||||
protected:
|
||||
|
||||
const Kernel::IKernelContext& m_kernelCtx;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "BoxAlgorithmProxy.h"
|
||||
#include "Chunk.h"
|
||||
#include "EncodedChunk.h"
|
||||
#include "Stream.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class DecoderBase
|
||||
* \brief Base, non-typed abstract class for decoders
|
||||
* \details Derived classes can be type-specific
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class DecoderBase
|
||||
{
|
||||
public:
|
||||
virtual bool decode(const EncodedChunk& chk) = 0;
|
||||
virtual ~DecoderBase() { }
|
||||
};
|
||||
|
||||
// Fwd declare, realized by Codecs*
|
||||
template <typename T>
|
||||
class DecoderImpl;
|
||||
|
||||
/**
|
||||
* \class Decoder
|
||||
* \brief Decoder for a specific type T.
|
||||
* \details The constructor is passed a stream to decode to. The decode() call pushes chunks to that stream.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
template <class T>
|
||||
class Decoder final : public DecoderBase
|
||||
{
|
||||
public:
|
||||
Decoder(const Kernel::IKernelContext& ctx, Stream<T>& stream) : m_stream(stream), m_decoder(ctx) { }
|
||||
|
||||
bool decode(const EncodedChunk& chk) override
|
||||
{
|
||||
m_decoder.decode(chk);
|
||||
if (m_decoder.isHeaderReceived()) { return m_decoder.getHeader(m_stream.getHeader()); }
|
||||
if (m_decoder.isBufferReceived())
|
||||
{
|
||||
typename T::Buffer* chunk = new typename T::Buffer();
|
||||
if (m_decoder.getBuffer(*chunk))
|
||||
{
|
||||
m_stream.push(chunk);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (m_decoder.isEndReceived()) { return m_decoder.getEnd(m_stream.getEnd()); }
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
Stream<T>& m_stream;
|
||||
DecoderImpl<T> m_decoder;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class DecoderAdapter
|
||||
* \brief This decoder is a wrapper over the decoders in OpenViBEToolkit.
|
||||
*
|
||||
* The toolkit decoders require a box context to run, and do not return class objects.
|
||||
* This decoder wrapper addresses those limitations.
|
||||
* The usage is to create e.g. Decoder<TypeSignal> dec; and use the member functions to
|
||||
* obtain the chunks with the signal type.
|
||||
*
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
template <class T, class TToolkitDecoder>
|
||||
class DecoderAdapter : protected Contexted
|
||||
{
|
||||
public:
|
||||
explicit DecoderAdapter(const Kernel::IKernelContext& ctx) : Contexted(ctx), m_box(ctx), m_impl(m_box, 0) {}
|
||||
~DecoderAdapter() override { }
|
||||
|
||||
virtual bool decode(const EncodedChunk& source)
|
||||
{
|
||||
if (source.m_Buffer.size() == 0)
|
||||
{
|
||||
// std::cout << "Empty chunk received\n";
|
||||
return false;
|
||||
}
|
||||
m_box.dummy.m_InBuffer.setSize(0, true);
|
||||
m_box.dummy.m_InBuffer.append(&source.m_Buffer[0], source.m_Buffer.size());
|
||||
m_lastStart = source.m_StartTime;
|
||||
m_lastEnd = source.m_EndTime;
|
||||
return m_impl.decode(0);
|
||||
}
|
||||
|
||||
virtual bool getHeader(typename T::Header& h)
|
||||
{
|
||||
setTimeStamps(h);
|
||||
return getHeaderImpl(h);
|
||||
}
|
||||
|
||||
virtual bool getBuffer(typename T::Buffer& b)
|
||||
{
|
||||
setTimeStamps(b);
|
||||
return getBufferImpl(b);
|
||||
}
|
||||
|
||||
virtual bool getEnd(typename T::End& e)
|
||||
{
|
||||
setTimeStamps(e);
|
||||
return getEndImpl(e);
|
||||
}
|
||||
|
||||
virtual bool isHeaderReceived() { return m_impl.isHeaderReceived(); }
|
||||
virtual bool isBufferReceived() { return m_impl.isBufferReceived(); }
|
||||
virtual bool isEndReceived() { return m_impl.isEndReceived(); }
|
||||
|
||||
protected:
|
||||
// These two must be written as specilizations (Codec*cpp)
|
||||
bool getHeaderImpl(typename T::Header& h);
|
||||
bool getBufferImpl(typename T::Buffer& b);
|
||||
|
||||
static bool getEndImpl(typename T::End& /*e*/) { return true; } // NOP regardless of type
|
||||
|
||||
bool setTimeStamps(Chunk& chunk) const
|
||||
{
|
||||
chunk.m_StartTime = m_lastStart;
|
||||
chunk.m_EndTime = m_lastEnd;
|
||||
return true;
|
||||
}
|
||||
|
||||
BoxAlgorithmProxy m_box;
|
||||
|
||||
CTime m_lastStart = CTime::min();
|
||||
CTime m_lastEnd = CTime::min();
|
||||
|
||||
TToolkitDecoder m_impl;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <stack>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <openvibe/CMatrix.hpp>
|
||||
|
||||
#include <socket/IConnectionServer.h>
|
||||
|
||||
#include <fs/Files.h>
|
||||
|
||||
#include <ebml/CReader.h>
|
||||
#include <ebml/CReaderHelper.h>
|
||||
|
||||
#include "StreamBundle.h"
|
||||
|
||||
#include "EBMLSourceFile.h"
|
||||
#include "Contexted.h"
|
||||
|
||||
#include "EncodedChunk.h"
|
||||
#include "Decoder.h"
|
||||
|
||||
class CClientHandler;
|
||||
class OutputEncoder;
|
||||
|
||||
class EBMLSource;
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class Demuxer
|
||||
* \brief Demuxes (and decodes) EBML streams
|
||||
* \details EBML containers such as .ov multiplex one or more streams. Demuxer takes an EBML source and splits and decodes it to a Stream Bundle.
|
||||
*
|
||||
* It has similar purpose as the Generic Stream Reader box.
|
||||
*
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class Demuxer final : public EBML::IReaderCallback, protected Contexted
|
||||
{
|
||||
public:
|
||||
|
||||
Demuxer(const Kernel::IKernelContext& ctx, EBMLSourceFile& origin, StreamBundle& target)
|
||||
: Contexted(ctx), m_origin(origin), m_target(target), m_reader(*this) { initialize(); }
|
||||
|
||||
bool initialize();
|
||||
bool uninitialize() const;
|
||||
|
||||
// Reads some data from the origin and decodes it to target. Returns false on EOF.
|
||||
bool step();
|
||||
static bool stop() { return true; }
|
||||
|
||||
protected:
|
||||
|
||||
EBMLSourceFile& m_origin;
|
||||
StreamBundle& m_target;
|
||||
|
||||
bool m_playingStarted = false;
|
||||
|
||||
EBML::CReader m_reader;
|
||||
EBML::CReaderHelper m_readerHelper;
|
||||
|
||||
EncodedChunk m_pendingChunk;
|
||||
|
||||
uint32_t m_chunksSent = 0;
|
||||
|
||||
bool m_pending = false;
|
||||
bool m_hasEBMLHeader = false;
|
||||
|
||||
std::stack<EBML::CIdentifier> m_nodes;
|
||||
std::map<uint32_t, uint32_t> m_streamIdxToOutputIdxs;
|
||||
std::map<uint32_t, CIdentifier> m_streamIdxToTypeIDs;
|
||||
|
||||
std::vector<size_t> m_chunkStreamType;
|
||||
|
||||
std::vector<DecoderBase*> m_decoders;
|
||||
|
||||
private:
|
||||
|
||||
bool isMasterChild(const EBML::CIdentifier& identifier) override;
|
||||
void openChild(const EBML::CIdentifier& identifier) override;
|
||||
void processChildData(const void* buffer, const size_t size) override;
|
||||
void closeChild() override;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+95
@@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
#include "../../../../plugins/processing/file-io/src/ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
#include <fs/Files.h>
|
||||
|
||||
#include "Contexted.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class EBMLSourceFile
|
||||
* \brief Reads bytes from an .ov file
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
* @note some ov files which have lots of stimulation chunks take a long time to import
|
||||
* when launching the Tracker from visual studio. this is probably due to memory allocation, similar to slow simple dsp grammar parsing.
|
||||
*
|
||||
*
|
||||
*/
|
||||
class EBMLSourceFile final : protected Contexted
|
||||
{
|
||||
public:
|
||||
explicit EBMLSourceFile(const Kernel::IKernelContext& ctx) : Contexted(ctx) { }
|
||||
|
||||
bool initialize(const char* signalFile)
|
||||
{
|
||||
log() << Kernel::LogLevel_Trace << "EBMLSource: Initializing with " << signalFile << "\n";
|
||||
|
||||
m_file = FS::Files::open(signalFile, "rb");
|
||||
if (!m_file) { return false; }
|
||||
|
||||
m_src = std::string(signalFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uninitialize()
|
||||
{
|
||||
log() << Kernel::LogLevel_Trace << "EBMLSource: Uninitializing\n";
|
||||
|
||||
if (m_file)
|
||||
{
|
||||
fclose(m_file);
|
||||
m_file = nullptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isEOF() const
|
||||
{
|
||||
if (!m_file) { return true; }
|
||||
return (feof(m_file) != 0 ? true : false);
|
||||
}
|
||||
|
||||
bool read(std::vector<uint8_t>& bytes, const size_t numBytes) const
|
||||
{
|
||||
if (!m_file)
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Error: No EBML source file set\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isEOF())
|
||||
{
|
||||
log() << Kernel::LogLevel_Trace << "EBML Source file EOF reached\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
bytes.resize(numBytes);
|
||||
const size_t s = fread(&bytes[0], sizeof(uint8_t), numBytes, m_file);
|
||||
bytes.resize(s); // array size tells how much was read
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string& getSource() const { return m_src; }
|
||||
|
||||
protected:
|
||||
std::string m_src;
|
||||
FILE* m_file = nullptr;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Chunk.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class EncodedChunk
|
||||
* \brief Data class for encoded chunks
|
||||
* \details
|
||||
* Stream content in OpenViBE is carried as encoded chunks. Although this inherits from Chunk,
|
||||
* this type is a bit special since its not explicitly have (header, buffer, end) subclasses. This is
|
||||
* because in its encoded form, the three are not differentiated but coded into bufferData.
|
||||
*
|
||||
* \note Here we use uint32_t for m_streamIdx as we assume it has fixed bitcount (unlike size_t which may change).
|
||||
*/
|
||||
class EncodedChunk : public Chunk
|
||||
{
|
||||
public:
|
||||
|
||||
// These are actually passed around in OpenViBE
|
||||
std::vector<uint8_t> m_Buffer;
|
||||
|
||||
// These are convenience information for Tracker
|
||||
uint64_t m_StreamType = 0;
|
||||
size_t m_StreamIndex = 0;
|
||||
};
|
||||
|
||||
// Note that we do not store this chunk type in EncodedChunk because when we receive EncodedChunks,
|
||||
// the type is not available until 'bufferData' has been decoded -- especially the case for the End type?
|
||||
enum class EChunkType
|
||||
{
|
||||
Header = 0,
|
||||
Buffer = 1,
|
||||
End = 2,
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
#pragma once
|
||||
|
||||
#include <string.h> // memcpy() on Ubuntu
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "EncodedChunk.h"
|
||||
|
||||
#include "BoxAlgorithmProxy.h"
|
||||
#include "Chunk.h"
|
||||
|
||||
#include "Stream.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class EncoderBase
|
||||
* \brief Base, non-typed abstract class for encoders
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class EncoderBase
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~EncoderBase() { }
|
||||
|
||||
// Encodes the current chunk of the attached stream (in the derived class)
|
||||
// chkType will contain details about the specific chunk type (since EncodedChunk doesn't show it explicitly)
|
||||
virtual bool encode(EncodedChunk& chk, EChunkType& chkType) = 0;
|
||||
|
||||
// Advance all timestamps of the encoded chunks by this amount
|
||||
// Used for catenating multiple tracks
|
||||
virtual bool setEncodeOffset(CTime newOffset) = 0;
|
||||
};
|
||||
|
||||
// Fwd declare, realized by Codecs*
|
||||
template <class T>
|
||||
class EncoderImpl;
|
||||
|
||||
/**
|
||||
* \class Encoder
|
||||
* \brief Encoder class taking in typed Stream and it into EBML-containing encoded chunks.
|
||||
* \details The constructor is passed a Stream to encode from. The encode() call pulls the next chunk from the Stream and returns an EncodedChunk.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
template <class T>
|
||||
class Encoder final : public EncoderBase
|
||||
{
|
||||
public:
|
||||
Encoder(const Kernel::IKernelContext& ctx, Stream<T>& stream) : m_stream(stream), m_encoder(ctx) { }
|
||||
|
||||
// Encode the current chunk. The caller must step the stream.
|
||||
bool encode(EncodedChunk& chk, EChunkType& chkType) override
|
||||
{
|
||||
const size_t position = m_stream.getPosition();
|
||||
|
||||
if (position == size_t(-1))
|
||||
{
|
||||
// std::cout << "Encode header\n";
|
||||
chkType = EChunkType::Header;
|
||||
return m_encoder.encodeHeader(chk, m_stream.getHeader());
|
||||
}
|
||||
if (position < m_stream.getChunkCount())
|
||||
{
|
||||
chkType = EChunkType::Buffer;
|
||||
|
||||
const bool retVal = m_encoder.encodeBuffer(chk, *m_stream.getChunk(position));
|
||||
// std::cout << "Encoder gave " << chk.bufferData.size() << "\n";
|
||||
|
||||
return retVal;
|
||||
}
|
||||
if (position == m_stream.getChunkCount())
|
||||
{
|
||||
chkType = EChunkType::End;
|
||||
|
||||
auto end = m_stream.getEnd();
|
||||
|
||||
// Some streams might not have an end in the file. Here we force one to be present.
|
||||
// @fixme move to load
|
||||
if (end.m_StartTime == CTime::max() || end.m_EndTime == CTime::max())
|
||||
{
|
||||
// @note modifies the stream
|
||||
end.m_StartTime = m_stream.getDuration();
|
||||
end.m_EndTime = end.m_StartTime;
|
||||
}
|
||||
|
||||
return m_encoder.encodeEnd(chk, end);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool setEncodeOffset(CTime newOffset) override { return m_encoder.setEncodeOffset(newOffset); }
|
||||
|
||||
protected:
|
||||
|
||||
Stream<T>& m_stream;
|
||||
EncoderImpl<T> m_encoder;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class EncoderAdapter
|
||||
* \brief Adapter to use implementations from Toolkit
|
||||
*
|
||||
* Note that this class does not set the encoded chunk times or index, these are currently
|
||||
* handled outside the encoder.
|
||||
*
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
template <class T, class TToolkitEncoder>
|
||||
class EncoderAdapter : protected Contexted
|
||||
{
|
||||
public:
|
||||
explicit EncoderAdapter(const Kernel::IKernelContext& ctx) : Contexted(ctx), m_box(ctx), m_impl(m_box, 0) {}
|
||||
~EncoderAdapter() override { }
|
||||
|
||||
// If chunk timestamps are adjusted, this function should be used to adjust any possible
|
||||
// timestamps inside the chunks accordingly
|
||||
bool setEncodeOffset(const CTime newOffset)
|
||||
{
|
||||
m_offset = newOffset;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool encodeHeader(EncodedChunk& chk, const typename T::Header& inputHdr)
|
||||
{
|
||||
m_box.dummy.setOutputChunkSize(0, 0, true);
|
||||
chk.m_StartTime = inputHdr.m_StartTime + m_offset;
|
||||
chk.m_EndTime = inputHdr.m_EndTime + m_offset;
|
||||
|
||||
encodeHeaderImpl(inputHdr);
|
||||
|
||||
fillChunk(chk);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool encodeBuffer(EncodedChunk& chk, const typename T::Buffer& inputBuf)
|
||||
{
|
||||
m_box.dummy.setOutputChunkSize(0, 0, true);
|
||||
chk.m_StartTime = inputBuf.m_StartTime + m_offset;
|
||||
chk.m_EndTime = inputBuf.m_EndTime + m_offset;
|
||||
|
||||
encodeBufferImpl(inputBuf);
|
||||
|
||||
fillChunk(chk);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool encodeEnd(EncodedChunk& chk, const typename T::End& inputEnd)
|
||||
{
|
||||
m_box.dummy.setOutputChunkSize(0, 0, true);
|
||||
|
||||
chk.m_StartTime = inputEnd.m_StartTime + m_offset;
|
||||
chk.m_EndTime = inputEnd.m_EndTime + m_offset;
|
||||
|
||||
encodeEndImpl(inputEnd);
|
||||
|
||||
fillChunk(chk);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// These two must be written as specializations (Codec*cpp)
|
||||
bool encodeHeaderImpl(const typename T::Header& hdr);
|
||||
bool encodeBufferImpl(const typename T::Buffer& buf);
|
||||
|
||||
bool encodeEndImpl(const typename T::End& /*end*/) { return m_impl.encodeEnd(); }
|
||||
|
||||
bool fillChunk(EncodedChunk& chk)
|
||||
{
|
||||
const size_t chunkSize = size_t(m_box.dummy.getOutputChunkSize(0));
|
||||
chk.m_Buffer.resize(chunkSize);
|
||||
if (chunkSize > 0) { memcpy(&chk.m_Buffer[0], m_box.dummy.getOutputChunkBuffer(0), chunkSize); }
|
||||
return true;
|
||||
}
|
||||
|
||||
BoxAlgorithmProxy m_box;
|
||||
|
||||
TToolkitEncoder m_impl;
|
||||
|
||||
CTime m_offset = CTime::min();
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
#pragma once
|
||||
|
||||
#include "CTracker.h"
|
||||
#include "Workspace.h"
|
||||
|
||||
#include "Stream.h"
|
||||
#include "TypeSignal.h"
|
||||
#include "TypeSpectrum.h"
|
||||
#include "TypeStimulation.h"
|
||||
|
||||
#include "Contexted.h"
|
||||
|
||||
#include "CLogListenerTracker.h"
|
||||
|
||||
// Forward declare
|
||||
struct _GtkBuilder;
|
||||
typedef struct _GtkWidget GtkWidget;
|
||||
// typedef struct _GtkTable GtkTable;
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
class StreamRendererBase;
|
||||
|
||||
/**
|
||||
* \class GUI
|
||||
* \brief The main GUI code for the OpenViBE Tracker.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
* \details
|
||||
* This file handles most of the GTK related GUI of the Tracker, with the actual functions modifying the data trying to be as much as possible
|
||||
* implemented in the non-GUI classes. Although the GUI is currently quite monolithic, the rendering of the different Stream types has been
|
||||
* delegated to StreamRenderer* classes, which in turn use the Mensia Advanced Visualizations toolkit. Code from Designer is also used by
|
||||
* making CMake include it.
|
||||
*
|
||||
*/
|
||||
class GUI final : protected Contexted
|
||||
{
|
||||
public:
|
||||
|
||||
GUI(int argc, char* argv[], CTracker& app);
|
||||
~GUI() override;
|
||||
|
||||
bool run();
|
||||
|
||||
/**
|
||||
* \class GUITrack
|
||||
* \brief Holds the renderers for each stream of a Track, and the correspondig GTK table for the widgets.
|
||||
* \author J. T. Lindgren
|
||||
*/
|
||||
class GUITrack
|
||||
{
|
||||
public:
|
||||
GUITrack() { }
|
||||
~GUITrack();
|
||||
|
||||
GtkWidget* m_Frame = nullptr;
|
||||
|
||||
// Renderer per stream
|
||||
std::vector<StreamRendererBase*> m_Renderers;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
// The main loop to do work, e.g. feed the tracks to processor
|
||||
bool step();
|
||||
|
||||
CString getWorkspaceInfo() const;
|
||||
|
||||
bool addTracksToMenu();
|
||||
|
||||
// Enable/disable certain widgets during play (processing)
|
||||
bool setPlaytimeWidgetState(bool enabled);
|
||||
// Enable/disable certain widgets depending on the current state (streams, processors, etc)
|
||||
bool updateIdleWidgetState();
|
||||
|
||||
bool storeRendererSettings();
|
||||
|
||||
// Callbacks
|
||||
bool addTrackCB();
|
||||
bool openWorkspaceCB();
|
||||
bool openProcessorCB() const;
|
||||
bool openProcessorCBFinal();
|
||||
bool saveAsCB();
|
||||
bool saveCB();
|
||||
bool incrementRevSaveCB();
|
||||
bool clearCB();
|
||||
bool quitCB();
|
||||
bool stopCB() const;
|
||||
bool playCB();
|
||||
bool playFastCB();
|
||||
bool workspaceInfoCB() const;
|
||||
bool aboutCB() const;
|
||||
|
||||
bool selectWorkspacePathCB() const;
|
||||
bool selectWorkspacePathCBFinal() const;
|
||||
|
||||
bool editSelectionCB();
|
||||
bool editSelectionCB2() { return editSelectionCB(); }
|
||||
bool selectAllCB();
|
||||
bool selectNoneCB();
|
||||
|
||||
bool processorPreferencesCB() const;
|
||||
bool processorPreferencesButtonOkCB();
|
||||
bool processorPreferencesButtonCancelCB() const;
|
||||
bool processorEditCB() const;
|
||||
bool processorEditCB2() const;
|
||||
|
||||
bool editNotesCB() const;
|
||||
bool editNotesCB2() const { return editNotesCB(); }
|
||||
|
||||
bool hScrollCB(GtkWidget* widget);
|
||||
bool hScaleCB(GtkWidget* widget);
|
||||
|
||||
bool deleteAllTracksCB();
|
||||
bool deleteTrackCB(GtkWidget* widget);
|
||||
bool moveTrackCB(GtkWidget* widget);
|
||||
bool toggleRulerCB(GtkWidget* widget);
|
||||
bool toggleShowSelectedOnlyCB(GtkWidget* widget);
|
||||
|
||||
static bool showChunksCB(GtkWidget* widget);
|
||||
bool moveStreamCB(GtkWidget* widget);
|
||||
bool deleteStreamCB(GtkWidget* widget);
|
||||
|
||||
bool applyBoxPluginCB(GtkWidget* widget);
|
||||
bool trackerPluginCB(GtkWidget* widget);
|
||||
bool setSelectionCB(GtkWidget* widget);
|
||||
|
||||
bool clearMessagesCB() const;
|
||||
|
||||
// Render a stream by pushing the chunks in view to the renderer
|
||||
template <typename T, typename TRenderer>
|
||||
bool draw(const Stream<T>* stream, TRenderer& renderer, CTime startTime, CTime endTime)
|
||||
{
|
||||
renderer.reset();
|
||||
|
||||
typename T::Buffer* buf;
|
||||
|
||||
// Push the visible chunks to renderer
|
||||
uint64_t chunksSent = 0;
|
||||
for (size_t i = 0; i < stream->getChunkCount(); ++i)
|
||||
{
|
||||
if (stream->getChunk(i, &buf) && buf->m_StartTime >= startTime && buf->m_EndTime <= endTime)
|
||||
{
|
||||
renderer.push(*buf);
|
||||
chunksSent++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
CTime viewDuration = (endTime - startTime).ceil();
|
||||
uint64_t chunkCount = viewDuration /renderer.getChunkDuration();
|
||||
|
||||
std::cout << "View duration " << viewDuration.toSeconds() << ", chks " << chunkCount << "\n";
|
||||
|
||||
std::cout << "Pushed " << chunksSent << " chks, padding " << chunkCount-chunksSent << " -> " << chunkCount << "\n";
|
||||
if (buf && chunksSent < chunkCount)
|
||||
{
|
||||
while (chunksSent < chunkCount)
|
||||
{
|
||||
// @fixme push empty
|
||||
renderer.push(*buf, true);
|
||||
chunksSent++;
|
||||
}
|
||||
}
|
||||
*/
|
||||
renderer.finalize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool redrawTrack(size_t index);
|
||||
bool redrawAllTracks();
|
||||
bool redrawStream(size_t trackIndex, size_t streamIndex);
|
||||
bool resetWidgetProperties();
|
||||
|
||||
bool initializeRenderers();
|
||||
bool clearRenderers();
|
||||
bool updateRulerState();
|
||||
|
||||
CTracker& m_tracker;
|
||||
|
||||
std::vector<GUITrack*> m_tracks; // One per each track in workspace
|
||||
|
||||
struct _GtkBuilder* m_interface = nullptr;
|
||||
|
||||
GtkWidget* m_mainWindow = nullptr;
|
||||
GtkWidget* m_scrollbar = nullptr;
|
||||
GtkWidget* m_scale = nullptr;
|
||||
GtkWidget* m_workspaceLabel = nullptr;
|
||||
GtkWidget* m_timeDisplay = nullptr;
|
||||
GtkWidget* m_trackCounter = nullptr;
|
||||
GtkWidget* m_selectionWindow = nullptr;
|
||||
|
||||
uint64_t m_numChannels = 0;
|
||||
uint64_t m_chunkSize = 0;
|
||||
uint64_t m_totalChunks = 0;
|
||||
|
||||
CTime m_previousTime = CTime::min();
|
||||
CTime m_lastRedraw = CTime::min();
|
||||
|
||||
#if defined(TARGET_OS_Windows)
|
||||
int m_requestResetInNFrames = -1;
|
||||
bool m_expanderOpen = false;
|
||||
double m_hScrollBarValue = 0;
|
||||
#endif
|
||||
|
||||
bool m_requestRedraw = false;
|
||||
bool m_waitingForStop = false;
|
||||
|
||||
CLogListenerTracker* m_logListener = nullptr;
|
||||
|
||||
// This hack is needed since on Ubuntu 16.04, for some reason
|
||||
// gtk_widget_set_sensitive doesn't work on main level menu items,
|
||||
// and descending the menu as a container doesn't seem to allow
|
||||
// graying out the menu items either
|
||||
std::vector<GtkWidget*> m_trackMenuWidgets;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "Contexted.h"
|
||||
#include "StreamBundle.h"
|
||||
#include "Workspace.h"
|
||||
#include "Selection.h"
|
||||
#include "ParallelExecutor.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class ITrackerPlugin
|
||||
* \brief Brief
|
||||
* \author J. T. Lindgren
|
||||
* \details
|
||||
*
|
||||
* Tracker Plugins are processing plugins specific to the OpenViBE Tracker.
|
||||
* The idea is to allow plugins to access the whole track content (StreamBundle)
|
||||
* with a simple interface and minimal overhead.
|
||||
*
|
||||
* In detail, the difference between Tracker Plugins and Box Plugins is as follows.
|
||||
* A box plugin wraps openvibe box code, and subsequently the code will have
|
||||
* to deal with the classical openvibe objects such as encoders, decoders, and so on.
|
||||
* The Tracker Plugin, on the other hand, gives the programmer access
|
||||
* to the StreamBundle structure. This allows the plugin code to request and manipulate
|
||||
* the streams and their chunks freely. A tracker plugin can also add or remove streams
|
||||
* to the bundle.
|
||||
*
|
||||
* Tracker plugins do not currently have explicit parameters, but you
|
||||
* can pass parameters in using configuration tokens, and then request/set them
|
||||
* via m_kernelCtx.getConfigurationManager() interface.
|
||||
*
|
||||
* Note that the StreamBundle given as input to a Tracker Plugin is in not
|
||||
* safeguarded against "bad" modifications by the plugin.
|
||||
*
|
||||
* @todo tracker and box plugins might be refactorable to be under same interface.
|
||||
* @todo allow non-inplace mode operation
|
||||
* @todo derive different subclasses for the 'capabilities' instead of one monolithic interface?
|
||||
*
|
||||
*/
|
||||
class ITrackerPlugin : protected Contexted
|
||||
{
|
||||
public:
|
||||
|
||||
// For the moment these are mutually exclusive capabilities; Tracker will prefer 'workspace' if the plugin supports it.
|
||||
enum class ECapabilities
|
||||
{
|
||||
Tracks = 1LL,
|
||||
Workspace = 2LL
|
||||
};
|
||||
|
||||
// Constructor
|
||||
explicit ITrackerPlugin(const Kernel::IKernelContext& ctx) : Contexted(ctx) { }
|
||||
|
||||
// @param track The input track. It can be modified by the process() call.
|
||||
// @return True on success, False otherwise
|
||||
// @note If the plugin changes the track, it must set the tracks 'dirty bit' as true.
|
||||
virtual bool process(StreamBundle& /*track*/) { return false; }
|
||||
|
||||
// @param wp The input workspace. It can be modified by the process() call.
|
||||
// @param exec A reference to a parallel executor that the plugin can use (if it prefers)
|
||||
// @return True on success, False otherwise
|
||||
// @note If the plugin changes any track in the workspace, it must set the tracks 'dirty bit' as true.
|
||||
// @note Passing the parallel executor as a parameter is not too neat, ideally it'd be part of IKernelContext but that'd require importing the exec to SDK.
|
||||
virtual bool process(Workspace& /*wp*/, ParallelExecutor& /*exec*/) { return false; }
|
||||
|
||||
// @param Capability to ask for
|
||||
// @return True if the plugin supports this
|
||||
// @note By default, the plugins are expected to have the track processing capability. If not, override this default.
|
||||
virtual bool hasCapability(const ECapabilities capability) { return (capability == ECapabilities::Tracks); }
|
||||
|
||||
// @return the name of the plugin
|
||||
virtual std::string getName() { return std::string("Unnamed"); }
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+102
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
|
||||
/*
|
||||
* Class allowing creating a set of worker threads that then pull jobs from a job list.
|
||||
*
|
||||
* Before this class can be used safely, various components in the OV SDK should be made
|
||||
* thread safe. These are at least
|
||||
* - LogManager
|
||||
* - CKernelObjectFactory
|
||||
* - AlgorithmManager?
|
||||
* - ConfigurationManager? (if any plugin adds tokens)
|
||||
*
|
||||
*/
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
typedef std::function<void(size_t)> jobCall;
|
||||
|
||||
// Fwd declare
|
||||
class CWorkerThread;
|
||||
|
||||
class ParallelExecutor
|
||||
{
|
||||
public:
|
||||
ParallelExecutor() { }
|
||||
bool initialize(const uint32_t nThreads);
|
||||
bool uninitialize();
|
||||
uint32_t getNumThreads() const { return m_threads.size(); }
|
||||
|
||||
// Clients call these
|
||||
bool pushJob(const jobCall& someJob); // add job, pass to threads
|
||||
bool pushJobList(const std::deque<jobCall>& vJobList); // add jobs
|
||||
bool clearPendingJobs(); // Delete all jobs thath haven't been launched yet
|
||||
bool waitForAll(); // wait until all pushed jobs are done
|
||||
size_t getJobCount() const; // Number of remaining jobs
|
||||
bool isIdle() const; // Nothing running & no tasks in list?
|
||||
|
||||
// Worker threads call these
|
||||
bool getJob(jobCall& job); // The call will hang until there is a job available or the executor is told to quit
|
||||
bool declareDone(); // Declare the previously obtained job as done
|
||||
|
||||
bool launchTest();
|
||||
|
||||
private:
|
||||
|
||||
mutable std::mutex m_jobMutex;
|
||||
|
||||
std::deque<jobCall> m_jobList;
|
||||
uint32_t m_nJobsRunning = 0;
|
||||
|
||||
std::condition_variable m_haveWork;
|
||||
std::condition_variable m_jobDone;
|
||||
|
||||
bool m_quit = false;
|
||||
|
||||
std::vector<CWorkerThread*> m_workerThreads;
|
||||
std::vector<std::thread*> m_threads;
|
||||
};
|
||||
|
||||
// Provides the worker threads a limited access to the parallel executor
|
||||
class ExecutorView
|
||||
{
|
||||
public:
|
||||
explicit ExecutorView(ParallelExecutor& exec) : m_exec(exec) { }
|
||||
bool getJob(jobCall& job) const { return m_exec.getJob(job); }
|
||||
bool declareDone() const { return m_exec.declareDone(); }
|
||||
|
||||
private:
|
||||
ParallelExecutor& m_exec;
|
||||
};
|
||||
|
||||
class CWorkerThread
|
||||
{
|
||||
public:
|
||||
CWorkerThread() { }
|
||||
|
||||
static void run(const ExecutorView ctx, const uint32_t threadNumber)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
jobCall job;
|
||||
if (!ctx.getJob(job)) { return; }
|
||||
|
||||
// @todo pass job failed to caller
|
||||
job(threadNumber);
|
||||
ctx.declareDone();
|
||||
}
|
||||
}
|
||||
|
||||
static void startWorkerThread(CWorkerThread* /*thread*/, const ExecutorView ctx, const uint32_t threadNumber) { run(ctx, threadNumber); }
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+141
@@ -0,0 +1,141 @@
|
||||
#pragma once
|
||||
|
||||
#include "StreamBundle.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include "Processor.h"
|
||||
#include "ProcExternalProcessingHelper.h" // Push and Pull clients
|
||||
|
||||
#include "Encoder.h"
|
||||
#include "Decoder.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class ProcExternalProcessing
|
||||
* \brief A processor implemented by passing data to/from External Processing Boxes inserted into Designer scenarios
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class ProcExternalProcessing final : public Processor
|
||||
{
|
||||
public:
|
||||
|
||||
explicit ProcExternalProcessing(const Kernel::IKernelContext& ctx) : Processor(ctx) {}
|
||||
ProcExternalProcessing(const Kernel::IKernelContext& ctx, const ProcExternalProcessing& other);
|
||||
|
||||
bool initialize(const std::string& xmlFile) override;
|
||||
bool uninitialize() override;
|
||||
|
||||
// Set targets for push and pull
|
||||
bool setNewSource(StreamBundle* source, bool sendHeader, bool sendEnd) override;
|
||||
bool setNewTarget(StreamBundle* target) override;
|
||||
|
||||
bool play(const bool playFast, const std::function<bool(CTime)>& quitCB) override { return play(playFast, quitCB, nullptr); }
|
||||
// @param nextTrackFun Callback to request more data (used for catenate mode)
|
||||
bool play(const bool playFast, const std::function<bool(CTime)>& quitCB, const std::function<bool()>& nextTrackFun);
|
||||
|
||||
// bool play(const bool playFast) override;
|
||||
bool stop() override;
|
||||
|
||||
bool setProcessorPorts(const uint32_t sendPort, const uint32_t recvPort) override
|
||||
{
|
||||
m_sendPort = sendPort;
|
||||
m_recvPort = recvPort;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getProcessorPorts(uint32_t& sendPort, uint32_t& recvPort) const override
|
||||
{
|
||||
sendPort = m_sendPort;
|
||||
recvPort = m_recvPort;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setProcessorFlags(const bool noGUI, const bool doSend, const bool doReceive)
|
||||
{
|
||||
m_noGUI = noGUI;
|
||||
m_doSend = doSend;
|
||||
m_doReceive = doReceive;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getProcessorFlags(bool& noGUI, bool& doSend, bool& doReceive) const
|
||||
{
|
||||
noGUI = m_noGUI;
|
||||
doSend = m_doSend;
|
||||
doReceive = m_doReceive;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool canPull() const { return m_doReceive; }
|
||||
bool canPush() const { return m_doSend; }
|
||||
|
||||
CTime getCurrentTime() const override;
|
||||
|
||||
bool isSynced() const
|
||||
{
|
||||
return (m_doSend ? (m_pushStartTime != BufferedClient::CLIENT_NOT_STARTED) : true) &&
|
||||
(m_doReceive ? (m_pullStartTime != BufferedClient::CLIENT_NOT_STARTED) : true);
|
||||
}
|
||||
|
||||
bool isRunning() const override { return m_isRunning; }
|
||||
|
||||
// Serialize state to configuration manager
|
||||
bool save() override;
|
||||
bool load() override;
|
||||
|
||||
protected:
|
||||
//bool connectClient(Communication::MessagingClient& client, uint32_t port);
|
||||
|
||||
// Send data to External Processing box
|
||||
bool push();
|
||||
// Receive data from External Processing box
|
||||
bool pop();
|
||||
|
||||
// n.b. for each external processing box, we need to have a separate thread since we don't know for
|
||||
// sure in which order OpenViBE will schedule their execution.
|
||||
PushClient* m_pushClient = nullptr;
|
||||
std::thread* m_pushClientThread = nullptr;
|
||||
CTime m_pushStartTime = BufferedClient::CLIENT_NOT_STARTED;
|
||||
|
||||
PullClient* m_pullClient = nullptr;
|
||||
std::thread* m_pullClientThread = nullptr;
|
||||
CTime m_pullStartTime = BufferedClient::CLIENT_NOT_STARTED;
|
||||
|
||||
std::thread* m_playerThread = nullptr;
|
||||
|
||||
bool m_sentSomething = false;
|
||||
bool m_isRunning = false;
|
||||
|
||||
uint64_t m_chunksSent = 0;
|
||||
CTime m_pushLastTime = CTime::min();
|
||||
CTime m_pullLastTime = CTime::min();
|
||||
|
||||
// if either port is zero, then the communication from the scenario is expected to flow to one direction only
|
||||
uint32_t m_sendPort = 50011;
|
||||
uint32_t m_recvPort = 50012;
|
||||
|
||||
bool m_doSend = true;
|
||||
bool m_doReceive = true;
|
||||
|
||||
// Used in continuous playing mode to get the start offset for the next stream
|
||||
CTime m_previousEnd = CTime::min();
|
||||
bool m_requestNewOffset = false;
|
||||
|
||||
bool m_sendHeader = true;
|
||||
bool m_sendEnd = true;
|
||||
|
||||
bool m_noGUI = true;
|
||||
|
||||
StreamBundle* m_src = nullptr;
|
||||
StreamBundle* m_dst = nullptr;
|
||||
|
||||
std::vector<EncoderBase*> m_encoders;
|
||||
std::vector<DecoderBase*> m_decoders;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include <communication/ovCMessagingClient.h>
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "EncodedChunk.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class BufferedClient
|
||||
* \brief A messaging client base class thats associated with a buffer of chunks.
|
||||
* \author J. T. Lindgren
|
||||
* \details
|
||||
*
|
||||
* The class is threaded. The thread connects to a single instance of OpenViBE's
|
||||
* External Processing Box that is assumed to be monitoring a specific port.
|
||||
*
|
||||
* The derived clients are one-directional: they only send or receive data.
|
||||
*
|
||||
*/
|
||||
class BufferedClient : protected Communication::MessagingClient
|
||||
{
|
||||
public:
|
||||
|
||||
explicit BufferedClient(const uint32_t port) : m_port(port) { }
|
||||
void requestQuit();
|
||||
bool hasQuit();
|
||||
void start();
|
||||
|
||||
CTime getStartTime()
|
||||
{
|
||||
// @todo might be a bit inefficient to have mutex here
|
||||
std::lock_guard<std::mutex> oLock(m_threadMutex);
|
||||
return m_startTime;
|
||||
}
|
||||
|
||||
uint64_t getTime() override { return MessagingClient::getTime(); }
|
||||
|
||||
static const CTime CLIENT_NOT_STARTED;
|
||||
|
||||
protected:
|
||||
|
||||
bool connectClient();
|
||||
|
||||
// Derived classes implement this to do either push or pull
|
||||
virtual bool step() = 0;
|
||||
|
||||
uint32_t m_port = 0;
|
||||
|
||||
// The mutex is used with the variables declared after it
|
||||
std::mutex m_threadMutex;
|
||||
std::condition_variable m_bufferCondition;
|
||||
|
||||
std::deque<EncodedChunk*> m_buffer;
|
||||
bool m_pleaseQuit = false;
|
||||
bool m_hasQuit = false;
|
||||
|
||||
// Time when the client has connected and synced, CTime(-1) if not yet
|
||||
CTime m_startTime = CLIENT_NOT_STARTED;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class PushClient
|
||||
* \brief A client dedicated to pushing data towards an External Processing Box
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class PushClient final : public BufferedClient
|
||||
{
|
||||
public:
|
||||
explicit PushClient(const uint32_t port) : BufferedClient(port) { }
|
||||
|
||||
// Append a chunk to be sent out
|
||||
bool pushBuffer(const EncodedChunk& encodedChunk);
|
||||
|
||||
// Force all pushed chunks to be sent
|
||||
void requestFlush();
|
||||
|
||||
protected:
|
||||
|
||||
// implements push
|
||||
bool step() override;
|
||||
|
||||
// Under the base class' mutex
|
||||
bool m_pleaseFlush = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \class PullClient
|
||||
* \brief A class dedicated to pulling data from an External Processing Box
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class PullClient final : public BufferedClient
|
||||
{
|
||||
public:
|
||||
explicit PullClient(const uint32_t port) : BufferedClient(port) { }
|
||||
|
||||
// Get the oldest chunk received. Returns false if none.
|
||||
bool pullBuffer(EncodedChunk& chunk);
|
||||
|
||||
bool isEndReceived() override { return BufferedClient::isEndReceived(); }
|
||||
|
||||
protected:
|
||||
|
||||
// implements pull
|
||||
bool step() override;
|
||||
|
||||
// Keep polling the sender for chunks as long as there is one available
|
||||
bool popMessagesToBuffer();
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <functional>
|
||||
|
||||
#include "StreamBundle.h"
|
||||
#include "Contexted.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class Processor
|
||||
* \author J. T. Lindgren
|
||||
* \brief A signal processing component that can receive/return data
|
||||
* \details
|
||||
*
|
||||
* Processor in OpenViBE tracker is a kind of bridge to Designer that is
|
||||
* used to send data in, do some processing with a scenario, and get the processed
|
||||
* data back. A processor can also be one-directional.
|
||||
*
|
||||
*
|
||||
*/
|
||||
class Processor : protected Contexted
|
||||
{
|
||||
public:
|
||||
|
||||
explicit Processor(const Kernel::IKernelContext& ctx) : Contexted(ctx) {}
|
||||
|
||||
// Set the processor XML file
|
||||
virtual bool initialize(const std::string& xmlFile) = 0;
|
||||
virtual bool uninitialize() = 0;
|
||||
|
||||
// Get the processor XML filename
|
||||
virtual const std::string& getFilename() const { return m_xmlFilename; }
|
||||
|
||||
// Connect the processor to a specific StreamBundle to read from
|
||||
virtual bool setNewSource(StreamBundle* source, bool sendHeader, bool sendEnd) = 0;
|
||||
// Connect the processor to a specific StreamBundle to write to
|
||||
virtual bool setNewTarget(StreamBundle* target) = 0;
|
||||
|
||||
// Launch Designer to configure the processor
|
||||
virtual bool configure(const char* filename); // If filename is NULL, use internal
|
||||
|
||||
// Launch the Player
|
||||
virtual bool play(const bool playFast, const std::function<bool(CTime)>& quitCallback) = 0;
|
||||
// Stop the Player
|
||||
virtual bool stop() = 0;
|
||||
virtual bool isRunning() const = 0;
|
||||
|
||||
virtual CTime getCurrentTime() const = 0;
|
||||
|
||||
// Communication port tcp/ip port IDs
|
||||
virtual bool setProcessorPorts(const uint32_t sendPort, const uint32_t recvPort) = 0;
|
||||
virtual bool getProcessorPorts(uint32_t& sendPort, uint32_t& recvPort) const = 0;
|
||||
|
||||
// Command line arguments passed to the processor (Designer) launch call
|
||||
bool setArguments(const char* args)
|
||||
{
|
||||
m_arguments = std::string(args);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* getArguments() const { return m_arguments.c_str(); }
|
||||
|
||||
// Serialize state to configuration manager
|
||||
virtual bool save() = 0;
|
||||
virtual bool load() = 0;
|
||||
|
||||
protected:
|
||||
std::string m_xmlFilename;
|
||||
std::string m_arguments;
|
||||
bool m_playFast = false;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "StreamBundle.h"
|
||||
#include "Contexted.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class Selection
|
||||
* \brief Some convenience functions for Track/Stream selections
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class Selection final : protected Contexted
|
||||
{
|
||||
public:
|
||||
Selection(const Kernel::IKernelContext& ctx, const std::vector<StreamBundle*>& tracks) : Contexted(ctx), m_tracks(tracks) { }
|
||||
|
||||
// Set the selection status of all streams on all tracks to the given state
|
||||
bool reset(bool state) const;
|
||||
|
||||
// Is any stream selected on the track?
|
||||
bool isTrackSelected(size_t track) const;
|
||||
// Nothing selectable?
|
||||
bool isEmpty() const;
|
||||
// Is something currently selected?
|
||||
bool isSomethingSelected() const;
|
||||
// Are selections of different tracks compatible?
|
||||
bool isSelectionConsistent() const;
|
||||
// How many tracks have at least one stream selected?
|
||||
size_t countSelectedTracks() const;
|
||||
// How many streams does a current track have selected?
|
||||
size_t countSelectedStreams(size_t trackIndex) const;
|
||||
|
||||
// Serialize state to ConfigurationManager
|
||||
bool save(const char* prefix) const;
|
||||
bool load(const char* prefix) const;
|
||||
|
||||
protected:
|
||||
|
||||
const std::vector<StreamBundle*>& m_tracks;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
// @author J.T. Lindgren
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "StreamBase.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
// Returns a copy of a stream with stimulations suggesting end of stream dropped
|
||||
std::shared_ptr<StreamBase> filterStimulationStreamEndPoints(const std::shared_ptr<const StreamBase>& src, const Kernel::IKernelContext& ctx);
|
||||
|
||||
// Returns a copy of a stream with given stimulations dropped
|
||||
std::shared_ptr<StreamBase> filterStimulationStream(const std::shared_ptr<const StreamBase>& src, const Kernel::IKernelContext& ctx,
|
||||
const std::vector<uint64_t>& stimsToFilter);
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include <ebml/IWriterHelper.h>
|
||||
#include <ebml/IWriter.h>
|
||||
#include <ebml/TWriterCallbackProxy.h>
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "StreamBase.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class Stream
|
||||
* \brief A container class representing a stream of OpenViBE
|
||||
* \author J. T. Lindgren
|
||||
* \details
|
||||
*
|
||||
* Stream is basically a specific kind of time-ordered container of timestamped, typed elements.
|
||||
*
|
||||
* Unlike OpenViBE streams in general, the Stream content in Tracker is stored in memory unencoded,
|
||||
* and hence if its content is written to an .ov file, passed to a box, or Designer, it must be encoded first.
|
||||
*
|
||||
* Stream must have a 'header' representing the stream parameters, a sequence of 'buffers'
|
||||
* containing the data, and an 'end' indicating the end of the stream.
|
||||
*
|
||||
* Time ----------------------->
|
||||
* 0
|
||||
* "front" [header][chk1][chk2]....[end] "tail"
|
||||
*
|
||||
* The stream additionally has a 'position' that always points to one of the above elements
|
||||
* and starts from front. Stepping the stream moves the stream position +1 chunk forward towards the tail.
|
||||
*
|
||||
* The stream API can be used in a way that chunks can be pushed
|
||||
* to the tail while the position counter is advancing from the head towards
|
||||
* the tail. Although this would allow interleaving pushes
|
||||
* and reads, currently the Tracker works in a way that when a track is loaded
|
||||
* or recorded, only push() operations are done until the finish. On the
|
||||
* other hand, when the track is sent out, only peek() and step() are used to
|
||||
* retrieve the chunks.
|
||||
*
|
||||
* Currently Tracker Plugins are allowed to access the tracks in a random access fashion,
|
||||
* as well as the GUI.
|
||||
*
|
||||
*
|
||||
*/
|
||||
template <class T>
|
||||
class Stream final : public StreamBase
|
||||
{
|
||||
public:
|
||||
explicit Stream(const Kernel::IKernelContext& ctx) : StreamBase(ctx) { m_end.m_StartTime = m_end.m_EndTime = CTime::max(); }
|
||||
|
||||
~Stream() override
|
||||
{
|
||||
/* if(m_Header) { delete m_Header; } */
|
||||
clear();
|
||||
}
|
||||
|
||||
CIdentifier getTypeIdentifier() const override { return T::getTypeIdentifier(); }
|
||||
|
||||
// Note: There is no corresponding setters, use the non-const versions to modify the header
|
||||
const typename T::Header& getHeader() const { return m_header; }
|
||||
const typename T::End& getEnd() const { return m_end; }
|
||||
typename T::Header& getHeader() { return m_header; }
|
||||
typename T::End& getEnd() { return m_end; }
|
||||
|
||||
bool push(typename T::Buffer* chunk)
|
||||
{
|
||||
m_chunks.push_back(chunk);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return the timestamps of the current chunk
|
||||
bool peek(CTime& startTime, CTime& endTime) const override { return peek(m_position, startTime, endTime); }
|
||||
|
||||
// Return timestamps of a specific chunk
|
||||
bool peek(size_t index, CTime& startTime, CTime& endTime) const override
|
||||
{
|
||||
if (index == size_t(-1)) { startTime = endTime = 0; }
|
||||
else if (index < m_chunks.size())
|
||||
{
|
||||
startTime = m_chunks[index]->m_StartTime;
|
||||
endTime = m_chunks[index]->m_EndTime;
|
||||
}
|
||||
else if (index == m_chunks.size() && m_chunks.size() > 0)
|
||||
{
|
||||
startTime = m_chunks[index - 1]->m_EndTime;
|
||||
endTime = m_chunks[index - 1]->m_EndTime;
|
||||
}
|
||||
else { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t getChunkCount() const override { return m_chunks.size(); }
|
||||
|
||||
bool getChunk(size_t idx, typename T::Buffer** ptr) const
|
||||
{
|
||||
if (idx < m_chunks.size())
|
||||
{
|
||||
*ptr = m_chunks[idx];
|
||||
return true;
|
||||
}
|
||||
*ptr = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
const typename T::Buffer* getChunk(size_t idx) const
|
||||
{
|
||||
if (idx < m_chunks.size()) { return m_chunks[idx]; }
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool clear() override
|
||||
{
|
||||
std::for_each(m_chunks.begin(), m_chunks.end(), [](typename T::Buffer* ptr) { delete ptr; });
|
||||
m_chunks.clear();
|
||||
m_position = size_t(-1);
|
||||
return true;
|
||||
}
|
||||
|
||||
CTime getDuration() const override
|
||||
{
|
||||
const size_t chunkCount = getChunkCount();
|
||||
if (chunkCount == 0) { return CTime::min(); }
|
||||
return m_chunks[chunkCount - 1]->m_EndTime;
|
||||
}
|
||||
|
||||
CTime getStartTime() const override
|
||||
{
|
||||
const size_t chunkCount = getChunkCount();
|
||||
if (chunkCount == 0) { return CTime::min(); }
|
||||
return m_chunks[0]->m_StartTime;
|
||||
}
|
||||
|
||||
// @fixme efficiency
|
||||
bool getOverlapping() const override
|
||||
{
|
||||
for (size_t i = 1; i < m_chunks.size(); ++i) { if (m_chunks[i]->m_StartTime < m_chunks[i - 1]->m_EndTime) { return true; } }
|
||||
return false;
|
||||
}
|
||||
|
||||
// @fixme efficiency
|
||||
bool getNoncontinuous() const override
|
||||
{
|
||||
for (size_t i = 1; i < m_chunks.size(); ++i) { if (m_chunks[i]->m_StartTime > m_chunks[i - 1]->m_EndTime) { return true; } }
|
||||
return false;
|
||||
}
|
||||
|
||||
// @fixme efficiency
|
||||
uint64_t countChunks(CTime startTime, CTime endTime) const
|
||||
{
|
||||
// Count the chunks @fixme not very efficient
|
||||
uint64_t chunkCount = 0;
|
||||
for (size_t i = 0; i < m_chunks.size(); ++i)
|
||||
{
|
||||
if (m_chunks[i]->m_StartTime >= startTime)
|
||||
{
|
||||
if (m_chunks[i]->m_EndTime <= endTime) { chunkCount++; }
|
||||
else { break; }
|
||||
}
|
||||
}
|
||||
return chunkCount;
|
||||
}
|
||||
|
||||
// Iterators and operators
|
||||
typename std::vector<typename T::Buffer*>::iterator begin() { return m_chunks.begin(); }
|
||||
typename std::vector<typename T::Buffer*>::iterator end() { return m_chunks.end(); }
|
||||
|
||||
typename T::Buffer& operator[](int idx) { return m_chunks[idx]; }
|
||||
|
||||
// @fixme
|
||||
// bool setHeader(TypeError::Header *) { return true; };
|
||||
// bool setBuffer(TypeError::Buffer *) { return true; };
|
||||
|
||||
// @fixme doesn't work yet
|
||||
bool copy(const StreamBase& other) override
|
||||
{
|
||||
if (T::getTypeIdentifier() != other.getTypeIdentifier()) { return false; }
|
||||
|
||||
const Stream<T>& otherStream = reinterpret_cast<const Stream<T>&>(other);
|
||||
|
||||
m_chunks.resize(otherStream.getChunkCount(), nullptr);
|
||||
|
||||
// @fixme this can be implemented when CMatrix has a working copy/assignment operators
|
||||
/*
|
||||
m_Header = otherStream.getHeader();
|
||||
|
||||
for (size_t c = 0; c < otherStream.getChunkCount(); ++c)
|
||||
{
|
||||
m_Chunks[c] = new T::Buffer(otherStream.getChunk(c));
|
||||
}
|
||||
|
||||
m_End = otherStream.getEnd();
|
||||
*/
|
||||
|
||||
log() << Kernel::LogLevel_Error << "Unimplemented method\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
typename T::Header m_header; // Header of a stream
|
||||
std::vector<typename T::Buffer*> m_chunks; // Buffers
|
||||
typename T::End m_end; // End of a stream
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory> // std::shared_ptr
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "Contexted.h"
|
||||
|
||||
// #include "ovkTAttributable.h"
|
||||
// class StreamBase : public OpenViBE::Kernel::TAttributable<Contexted> {
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
|
||||
/**
|
||||
* \class StreamBase
|
||||
* \brief Abstract, non-typed base class for Streams.
|
||||
* \details For details, please see the type-specific derived class Stream<T>.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class StreamBase : protected Contexted
|
||||
{
|
||||
public:
|
||||
explicit StreamBase(const Kernel::IKernelContext& ctx) : Contexted(ctx) { }
|
||||
~StreamBase() override { }
|
||||
|
||||
virtual CIdentifier getTypeIdentifier() const = 0;
|
||||
|
||||
virtual bool peek(CTime& startTime, CTime& endTime) const = 0;
|
||||
virtual bool peek(size_t index, CTime& startTime, CTime& endTime) const = 0;
|
||||
|
||||
virtual bool step()
|
||||
{
|
||||
if (m_position <= getChunkCount() || m_position == size_t(-1))
|
||||
{
|
||||
m_position++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool reset()
|
||||
{
|
||||
m_position = size_t(-1);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool clear() = 0;
|
||||
|
||||
virtual size_t getChunkCount() const = 0;
|
||||
|
||||
virtual CTime getDuration() const = 0;
|
||||
virtual CTime getStartTime() const = 0;
|
||||
|
||||
// Current play position
|
||||
size_t getPosition() const { return m_position; }
|
||||
|
||||
bool setPosition(const size_t position)
|
||||
{
|
||||
m_position = position;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Is the stream currently selected? (nb. this is not in the .ov file)
|
||||
bool getSelected() const { return m_selected; }
|
||||
|
||||
bool setSelected(const bool newState)
|
||||
{
|
||||
m_selected = newState;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stream characteristics
|
||||
virtual bool getOverlapping() const = 0;
|
||||
virtual bool getNoncontinuous() const = 0;
|
||||
|
||||
virtual bool copy(const StreamBase& /*other*/) { return false; }
|
||||
|
||||
protected:
|
||||
|
||||
size_t m_position = size_t(-1); // -1 == beginning of the stream, header
|
||||
|
||||
// Is the stream currently selected?
|
||||
bool m_selected = true;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<StreamBase> StreamPtr;
|
||||
typedef std::shared_ptr<const StreamBase> StreamPtrConst;
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <memory> // shared_ptr
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "StreamBase.h"
|
||||
|
||||
#include "Contexted.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamBundle
|
||||
* \brief StreamBundle is a container of one or more typed streams. It corresponds to a 'track' in Tracker and can represent an .ov file.
|
||||
* \details
|
||||
*
|
||||
* StreamBundle can be queried for streams in time order: getNextStream() call can be used to find out
|
||||
* the stream which has the earliest chunk position pointer (in time). Stepping the stream may make some
|
||||
* other stream to be returned on the next get call.
|
||||
*
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class StreamBundle final : protected Contexted
|
||||
{
|
||||
public:
|
||||
|
||||
explicit StreamBundle(const Kernel::IKernelContext& ctx) : Contexted(ctx) {}
|
||||
|
||||
// Copy everything, allocate new memory for content
|
||||
bool deepCopy(const StreamBundle& other);
|
||||
// Copy selected subset of streams from "other".
|
||||
// Other will retain ownership of any internal pointers.
|
||||
// Since the copy gets write access to content of other, "other" is not const.
|
||||
bool copyFrom(StreamBundle& other);
|
||||
|
||||
bool initialize();
|
||||
bool uninitialize();
|
||||
|
||||
// Rewind all streams
|
||||
bool rewind();
|
||||
|
||||
// Returns the stream which has a position with the earliest beginning timestamp
|
||||
StreamPtr getNextStream(size_t& index);
|
||||
bool getNextStreamIndex(size_t& index) const;
|
||||
|
||||
size_t getNumStreams() const { return m_streams.size(); }
|
||||
StreamPtrConst getStream(const size_t idx) const { return (idx < m_streams.size()) ? m_streams[idx] : nullptr; }
|
||||
StreamPtr getStream(const size_t idx) { return (idx < m_streams.size()) ? m_streams[idx] : nullptr; }
|
||||
std::vector<StreamPtr>& getAllStreams() { return m_streams; }
|
||||
|
||||
// A factory method that creates a stream of a specific type into the slot index
|
||||
bool createStream(size_t index, const CIdentifier& typeID);
|
||||
bool deleteStream(size_t index);
|
||||
bool setStream(size_t index, const std::shared_ptr<StreamBase>& ptr);
|
||||
bool swapStreams(size_t idx1, size_t idx2);
|
||||
bool moveStream(size_t srcIdx, size_t dstIdx);
|
||||
|
||||
// Returns the duration of the longest stream in the bundle
|
||||
CTime getMaxDuration() const;
|
||||
|
||||
// Have all streams ended in the bundle?
|
||||
bool isFinished() const
|
||||
{
|
||||
size_t dummy;
|
||||
return !getNextStreamIndex(dummy);
|
||||
}
|
||||
|
||||
// The name of the .ov file this bundle corresponds to
|
||||
const std::string& getSource() const { return m_source; }
|
||||
void setSource(const std::string& src) { m_source = src; }
|
||||
bool getDirtyBit() const { return m_dirty; }
|
||||
void setDirtyBit(const bool newState) { m_dirty = newState; }
|
||||
|
||||
protected:
|
||||
|
||||
// As StreamBundles can consist of streams from diverse origins where some of them would
|
||||
// like to pass the ownership and some to keep it, instead of writing messy ownership tracking code,
|
||||
// we use shared pointers for streams.
|
||||
std::vector<StreamPtr> m_streams;
|
||||
|
||||
std::string m_source; // Identifies the .ov file of the bundle on disk
|
||||
|
||||
bool m_dirty = true; // True if the stream has not been saved to disk after last modification
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Masterarbeit/openvibe/extras-master/applications/platform/tracker/include/StreamBundleImportExport.h
Executable
+20
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
//
|
||||
// n.b. having this as a separate file so EBML/Demuxer dependencies do not get pulled into StreamBundle
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "StreamBundle.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
// These functions import/export stream bundles from .ov files
|
||||
StreamBundle* readStreamBundleFromFile(const Kernel::IKernelContext& ctx, const char* filename, bool memorySaveMode);
|
||||
bool saveStreamBundleToFile(const Kernel::IKernelContext& ctx, StreamBundle* track, const char* filename);
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CodecsAll.h"
|
||||
|
||||
#include "Stream.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamFactory
|
||||
* \brief A factory method returning an empty Stream object of the requested type
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class StreamFactory
|
||||
{
|
||||
public:
|
||||
// Factory
|
||||
static StreamPtr getStream(const Kernel::IKernelContext& ctx, const CIdentifier& typeID)
|
||||
{
|
||||
std::shared_ptr<StreamBase> stream;
|
||||
// @todo others
|
||||
if (typeID == OVTK_TypeId_StreamedMatrix) { stream = std::make_shared<Stream<TypeMatrix>>(ctx); }
|
||||
else if (typeID == OVTK_TypeId_Signal) { stream = std::make_shared<Stream<TypeSignal>>(ctx); }
|
||||
else if (typeID == OVTK_TypeId_Stimulations) { stream = std::make_shared<Stream<TypeStimulation>>(ctx); }
|
||||
else if (typeID == OVTK_TypeId_ExperimentInfo) { stream = std::make_shared<Stream<TypeExperimentInfo>>(ctx); }
|
||||
else if (typeID == OV_TypeId_ChannelLocalisation
|
||||
) { stream = std::make_shared<Stream<TypeChannelLocalization>>(ctx); } // bug in toolkit atm with OVTK_ define
|
||||
else if (typeID == OV_TypeId_ChannelUnits) { stream = std::make_shared<Stream<TypeChannelUnits>>(ctx); } // bug in toolkit atm with OVTK_ define
|
||||
else if (typeID == OVTK_TypeId_Spectrum) { stream = std::make_shared<Stream<TypeSpectrum>>(ctx); }
|
||||
else if (typeID == OVTK_TypeId_FeatureVector) { stream = std::make_shared<Stream<TypeFeatureVector>>(ctx); }
|
||||
else
|
||||
{
|
||||
ctx.getLogManager() << Kernel::LogLevel_Info << "Warning: Unknown stream type "
|
||||
<< ctx.getTypeManager().getTypeName(typeID) << " " << typeID.str() << "\n";
|
||||
stream = std::make_shared<Stream<TypeError>>(ctx);
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
// Prevent constructing
|
||||
StreamFactory() = delete;
|
||||
StreamFactory(const StreamFactory&) = delete;
|
||||
StreamFactory(StreamFactory&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+248
@@ -0,0 +1,248 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <iostream>
|
||||
#include <memory> // shared_ptr
|
||||
|
||||
#include <mensia/advanced-visualization.hpp>
|
||||
#include <mTGtkGLWidget.hpp>
|
||||
#include <m_GtkGL.hpp>
|
||||
|
||||
#include <mIRuler.hpp>
|
||||
|
||||
#include "Stream.h"
|
||||
#include "Contexted.h"
|
||||
|
||||
typedef struct _GtkWidget GtkWidget;
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererBase
|
||||
* \brief Abstract, non-typed class for visually rendering the Streams in the Tracker GUI
|
||||
* \details See the derived, typed versions of the class
|
||||
*
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class StreamRendererBase : protected Contexted
|
||||
{
|
||||
public:
|
||||
explicit StreamRendererBase(const Kernel::IKernelContext& ctx) : Contexted(ctx) { }
|
||||
~StreamRendererBase() override { }
|
||||
|
||||
// Calls for API users
|
||||
|
||||
// Initialize the renderer
|
||||
virtual bool initialize();
|
||||
virtual bool uninitialize();
|
||||
|
||||
// Show the windows
|
||||
virtual bool realize();
|
||||
|
||||
virtual bool setTitle(const char* title);
|
||||
|
||||
// Draw chunks in range [startTime,endTime]
|
||||
virtual bool spool(const CTime startTime, const CTime endTime) = 0;
|
||||
|
||||
virtual CString renderAsText(const size_t indent = 0) const;
|
||||
|
||||
virtual bool showChunkList()
|
||||
{
|
||||
log() << Kernel::LogLevel_Warning << "Chunk listing not implemented for stream type (or unavailable in current Tracker mode)\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual CTime getChunkDuration() const { return m_chunkDuration; }
|
||||
|
||||
virtual bool setRulerVisibility(bool isVisible);
|
||||
|
||||
// Save/load settings as configuration manager tokens
|
||||
// The tokens loaded/saved will have the given prefix
|
||||
virtual bool restoreSettings(const std::string& prefix);
|
||||
virtual bool storeSettings(const std::string& prefix);
|
||||
|
||||
GtkWidget* getWidget() const { return m_main; }
|
||||
|
||||
// GtkGlWidget calls the following
|
||||
|
||||
virtual bool redraw(bool bImmediate = false);
|
||||
virtual bool reshape(uint32_t width, uint32_t height);
|
||||
virtual bool draw();
|
||||
virtual bool preDraw();
|
||||
virtual bool postDraw();
|
||||
virtual void drawLeft();
|
||||
virtual void drawRight();
|
||||
virtual void drawBottom();
|
||||
virtual bool mouseButton(int x, int y, int button, int status);
|
||||
virtual bool mouseMotion(int x, int y);
|
||||
|
||||
virtual bool keyboard(uint32_t /*width*/, uint32_t /*height*/, uint32_t /*value*/, bool /*unused*/)
|
||||
{
|
||||
std::cout << "keyb requested\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
// Resets the renderer aperture (number of samples/chunks)
|
||||
virtual bool reset(CTime startTime, CTime endTime) = 0;
|
||||
virtual bool updateRulerVisibility();
|
||||
// After pushing samples, request rebuild & redraw
|
||||
virtual bool finalize();
|
||||
|
||||
CTime m_startTime = CTime::min();
|
||||
CTime m_endTime = CTime::min();
|
||||
CTime m_chunkDuration = CTime::min();
|
||||
|
||||
uint32_t m_width = 640;
|
||||
uint32_t m_height = 480;
|
||||
uint32_t m_textureID = 0;
|
||||
|
||||
bool m_rotate = false;
|
||||
bool m_isScaleVisible = true;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float r = 0, g = 0, b = 0;
|
||||
} color_t;
|
||||
|
||||
color_t m_color;
|
||||
|
||||
GtkWidget* m_viewport = nullptr;
|
||||
GtkWidget* m_main = nullptr;
|
||||
GtkWidget* m_top = nullptr;
|
||||
GtkWidget* m_left = nullptr;
|
||||
GtkWidget* m_right = nullptr;
|
||||
GtkWidget* m_bottom = nullptr;
|
||||
GtkWidget* m_cornerLeft = nullptr;
|
||||
GtkWidget* m_cornerRight = nullptr;
|
||||
|
||||
std::string m_title;
|
||||
|
||||
std::vector<AdvancedVisualization::IRenderer*> m_renderers;
|
||||
|
||||
AdvancedVisualization::CRendererContext* m_rendererCtx = nullptr;
|
||||
AdvancedVisualization::CRendererContext* m_subRendererCtx = nullptr;
|
||||
|
||||
AdvancedVisualization::TGtkGLWidget<StreamRendererBase> m_gtkGLWidget;
|
||||
|
||||
AdvancedVisualization::IRuler* m_ruler = nullptr;
|
||||
|
||||
// mouse related
|
||||
std::map<int, int> m_buttons;
|
||||
int m_mouseX = 0;
|
||||
int m_mouseY = 0;
|
||||
bool m_mouseInitialized = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class spoolImpl
|
||||
* \brief Push chunks in a specified interval to a renderer
|
||||
* \details
|
||||
*
|
||||
* A generic template that is used internally by the classes derived from StreamRendererBase
|
||||
* to implement spool(t1,t2) : pushing a [t1,t2] interval of chunks to the renderer.
|
||||
*
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
template <typename T, typename TRenderer>
|
||||
bool spoolImpl(std::shared_ptr<const Stream<T>> stream, TRenderer& renderer, CTime startTime, CTime endTime)
|
||||
{
|
||||
renderer.reset(startTime, endTime);
|
||||
|
||||
typename T::Buffer* buf;
|
||||
|
||||
// Push the visible chunks to renderer
|
||||
uint64_t chunksSent = 0;
|
||||
for (size_t i = 0; i < stream->getChunkCount(); ++i)
|
||||
{
|
||||
if (stream->getChunk(i, &buf) && buf->m_StartTime >= startTime && buf->m_EndTime <= endTime)
|
||||
{
|
||||
renderer.push(*buf);
|
||||
chunksSent++;
|
||||
}
|
||||
}
|
||||
|
||||
renderer.finalize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helper function for showMatrixList(), adds a column to a tree view, @fixme move somewhere else
|
||||
void add_column(GtkTreeView* treeView, const char* name, uint32_t id, uint32_t minWidth);
|
||||
|
||||
/**
|
||||
* \class showMatrixList
|
||||
* \brief Generic template that can be used to draw lists of any stream that derives from the matrix type.
|
||||
* @fixme might refactor somewhere else
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
template <typename T>
|
||||
bool showMatrixList(std::shared_ptr<const Stream<T>> stream, GtkWidget** listWindow, const char* title)
|
||||
{
|
||||
if (*listWindow)
|
||||
{
|
||||
gtk_window_present(GTK_WINDOW(*listWindow));
|
||||
return true;
|
||||
}
|
||||
|
||||
GtkBuilder* builder = gtk_builder_new();
|
||||
const CString filename = Directories::getDataDir() + "/applications/tracker/tracker.ui";
|
||||
if (!gtk_builder_add_from_file(builder, filename, nullptr))
|
||||
{
|
||||
std::cout << "Problem loading [" << filename << "]\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
*listWindow = GTK_WIDGET(gtk_builder_get_object(builder, "tracker-stimulation_list"));
|
||||
GtkTreeView* treeView = GTK_TREE_VIEW(gtk_builder_get_object(builder, "tracker-stimulation_list-treeview"));
|
||||
// GtkListStore* listStore = GTK_LIST_STORE(gtk_builder_get_object(pBuilder, "liststore_select"));
|
||||
GtkTreeStore* treeStore = gtk_tree_store_new(4, G_TYPE_UINT, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_STRING);
|
||||
|
||||
gtk_window_set_title(GTK_WINDOW(*listWindow), title);
|
||||
|
||||
add_column(treeView, "Chunk#", 0, 10);
|
||||
add_column(treeView, "ChunkStart (s)", 1, 5);
|
||||
add_column(treeView, "ChunkEnd (s)", 2, 5);
|
||||
add_column(treeView, "Chunk dims", 3, 10);
|
||||
|
||||
gtk_tree_view_set_model(treeView, GTK_TREE_MODEL(treeStore));
|
||||
|
||||
GtkTreeIter it;
|
||||
gtk_tree_store_clear(treeStore);
|
||||
|
||||
// ::gtk_tree_view_set_model(m_pChannelTreeView, nullptr);
|
||||
for (size_t i = 0; i < stream->getChunkCount(); ++i)
|
||||
{
|
||||
const typename T::Buffer* ptr = stream->getChunk(i);
|
||||
if (!ptr) { break; }
|
||||
|
||||
std::stringstream ss;
|
||||
ss << ptr->m_buffer.getDimensionCount() << " : ";
|
||||
for (uint32_t j = 0; j < ptr->m_buffer.getDimensionCount(); ++j) { ss << (j > 0 ? " x " : "") << ptr->m_buffer.getDimensionSize(j); }
|
||||
|
||||
gtk_tree_store_append(treeStore, &it, nullptr);
|
||||
::gtk_tree_store_set(treeStore, &it, 0, i, 1, ptr->m_StartTime.toSeconds(), 2, ptr->m_EndTime.toSeconds(), 3, ss.str().c_str(), -1);
|
||||
}
|
||||
|
||||
// Hide instead of destroy on closing the window
|
||||
g_signal_connect(*listWindow, "delete_event", G_CALLBACK(gtk_widget_hide_on_delete), nullptr);
|
||||
gtk_widget_show_all(GTK_WIDGET(*listWindow));
|
||||
g_object_unref(builder);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StreamRendererLabel.h"
|
||||
#include "TypeChannelLocalization.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererChannelLocalization
|
||||
* \brief Renderer for Channel Localization streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
* @ fixme this class inherits a lot of junk from the root class which is not initialized properly and
|
||||
* hence cannot be used. but if just the interface below is used, its alright. Can't make base class
|
||||
* private as we store the renderer as a pointer to the base.
|
||||
*
|
||||
*/
|
||||
class StreamRendererChannelLocalization final : public StreamRendererLabel
|
||||
{
|
||||
public:
|
||||
StreamRendererChannelLocalization(const Kernel::IKernelContext& ctx, const std::shared_ptr<const Stream<TypeChannelLocalization>>& stream)
|
||||
: StreamRendererLabel(ctx), m_Stream(stream) { }
|
||||
|
||||
CString renderAsText(const size_t indent) const override;
|
||||
|
||||
bool showChunkList() override;
|
||||
|
||||
protected:
|
||||
|
||||
std::shared_ptr<const Stream<TypeChannelLocalization>> m_Stream;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StreamRendererLabel.h"
|
||||
#include "TypeChannelUnits.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererChannelUnits
|
||||
* \brief Renderer for Channel Units streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
* @ fixme this class inherits a lot of junk from the root class which is not initialized properly and
|
||||
* hence cannot be used. but if just the interface below is used, its alright. Can't make base class
|
||||
* private as we store the renderer as a pointer to the base.
|
||||
*
|
||||
*/
|
||||
class StreamRendererChannelUnits final : public StreamRendererLabel
|
||||
{
|
||||
public:
|
||||
StreamRendererChannelUnits(const Kernel::IKernelContext& ctx, const std::shared_ptr<const Stream<TypeChannelUnits>>& stream)
|
||||
: StreamRendererLabel(ctx), m_stream(stream) { }
|
||||
|
||||
CString renderAsText(const size_t indent) const override;
|
||||
bool showChunkList() override;
|
||||
|
||||
protected:
|
||||
|
||||
std::shared_ptr<const Stream<TypeChannelUnits>> m_stream;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StreamRendererLabel.h"
|
||||
#include "TypeExperimentInfo.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererExperimentInfo
|
||||
* \brief Renderer for Experiment Information streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
* @ fixme this class inherits a lot of junk from the root class which is not initialized properly and
|
||||
* hence cannot be used. but if just the interface below is used, its alright. Can't make base class
|
||||
* private as we store the renderer as a pointer to the base.
|
||||
*
|
||||
*/
|
||||
class StreamRendererExperimentInfo final : public StreamRendererLabel
|
||||
{
|
||||
public:
|
||||
StreamRendererExperimentInfo(const Kernel::IKernelContext& ctx, const std::shared_ptr<const Stream<TypeExperimentInfo>>& stream)
|
||||
: StreamRendererLabel(ctx), m_stream(stream) { }
|
||||
|
||||
CString renderAsText(const size_t indent) const override;
|
||||
|
||||
bool showChunkList() override;
|
||||
|
||||
protected:
|
||||
|
||||
std::shared_ptr<const Stream<TypeExperimentInfo>> m_stream;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "StreamRendererMatrix.h"
|
||||
#include "StreamRendererSignal.h"
|
||||
#include "StreamRendererStimulation.h"
|
||||
#include "StreamRendererSpectrum.h"
|
||||
#include "StreamRendererExperimentInfo.h"
|
||||
#include "StreamRendererLabel.h"
|
||||
#include "StreamRendererChannelUnits.h"
|
||||
#include "StreamRendererChannelLocalization.h"
|
||||
#include "StreamRendererNothing.h"
|
||||
|
||||
#include "Stream.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererFactory
|
||||
* \brief Factory method for getting Stream Renderers of different types
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class StreamRendererFactory
|
||||
{
|
||||
public:
|
||||
|
||||
// Factory special case
|
||||
static StreamRendererBase* getDummyRenderer(const Kernel::IKernelContext& ctx) { return new StreamRendererNothing(ctx); }
|
||||
|
||||
// Generic factory
|
||||
static StreamRendererBase* getRenderer(const Kernel::IKernelContext& ctx, const StreamPtrConst& stream)
|
||||
{
|
||||
if (!stream) { ctx.getLogManager() << Kernel::LogLevel_Warning << "Nullptr stream, using label renderer\n"; }
|
||||
|
||||
const CIdentifier typeID = (stream ? stream->getTypeIdentifier() : CIdentifier::undefined());
|
||||
|
||||
StreamRendererBase* renderer;
|
||||
|
||||
if (typeID == OV_TypeId_Signal) { renderer = new StreamRendererSignal(ctx, std::static_pointer_cast<const Stream<TypeSignal>>(stream)); }
|
||||
else if (typeID == OV_TypeId_Spectrum) { renderer = new StreamRendererSpectrum(ctx, std::static_pointer_cast<const Stream<TypeSpectrum>>(stream)); }
|
||||
else if (typeID == OV_TypeId_Stimulations)
|
||||
{
|
||||
// @todo should be pushed to all other renderers
|
||||
renderer = new StreamRendererStimulation(ctx, std::static_pointer_cast<const Stream<TypeStimulation>>(stream));
|
||||
}
|
||||
else if (typeID == OV_TypeId_StreamedMatrix) { renderer = new StreamRendererMatrix(ctx, std::static_pointer_cast<const Stream<TypeMatrix>>(stream)); }
|
||||
else if (typeID == OV_TypeId_FeatureVector)
|
||||
{
|
||||
// Since featurevector is just a constrained matrix, we use the matrix renderer
|
||||
renderer = new StreamRendererMatrix(ctx, std::static_pointer_cast<const Stream<TypeMatrix>>(stream));
|
||||
}
|
||||
else if (typeID == OV_TypeId_ExperimentInfo)
|
||||
{
|
||||
// Since featurevector is just a constrained matrix, we use the matrix renderer
|
||||
renderer = new StreamRendererExperimentInfo(ctx, std::static_pointer_cast<const Stream<TypeExperimentInfo>>(stream));
|
||||
}
|
||||
else if (typeID == OV_TypeId_ChannelUnits)
|
||||
{
|
||||
renderer = new StreamRendererChannelUnits(ctx, std::static_pointer_cast<const Stream<TypeChannelUnits>>(stream));
|
||||
}
|
||||
else if (typeID == OV_TypeId_ChannelLocalisation)
|
||||
{
|
||||
renderer = new StreamRendererChannelLocalization(ctx, std::static_pointer_cast<const Stream<TypeChannelLocalization>>(stream));
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.getLogManager() << Kernel::LogLevel_Trace << "Using label renderer for stream of type " << ctx.getTypeManager().getTypeName(typeID) << "\n";
|
||||
renderer = new StreamRendererLabel(ctx);
|
||||
}
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
// Prevent constructing
|
||||
StreamRendererFactory() = delete;
|
||||
StreamRendererFactory(const StreamRendererFactory&) = delete;
|
||||
StreamRendererFactory(StreamRendererFactory&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StreamRendererBase.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererLabel
|
||||
* \brief Renderer for any stream that is simply visualized as a labeled widget
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
* @ fixme this class inherits a lot of junk from the base class which is not initialized properly and
|
||||
* hence cannot be used. but if just the interface below is used, its alright. Can't make base class
|
||||
* private as we store the renderer as a pointer to the base.
|
||||
*
|
||||
*/
|
||||
class StreamRendererLabel : public StreamRendererBase
|
||||
{
|
||||
public:
|
||||
explicit StreamRendererLabel(const Kernel::IKernelContext& ctx) : StreamRendererBase(ctx) { }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override { return true; }
|
||||
bool setTitle(const char* title) override;
|
||||
|
||||
bool spool(const CTime /*startTime*/, const CTime /*endTime*/) override { return true; }
|
||||
|
||||
bool setRulerVisibility(bool /*isVisible*/) override { return true; }
|
||||
|
||||
virtual bool showChunkList(const char* title);
|
||||
|
||||
protected:
|
||||
|
||||
bool reset(CTime /*startTime*/, CTime /*endTime*/) override { return true; }
|
||||
bool realize() override { return true; }
|
||||
|
||||
GtkWidget* m_label = nullptr;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StreamRendererBase.h"
|
||||
#include "TypeMatrix.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererMatrix
|
||||
* \brief Renderer for Matrix streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class StreamRendererMatrix final : public StreamRendererBase
|
||||
{
|
||||
public:
|
||||
StreamRendererMatrix(const Kernel::IKernelContext& ctx, std::shared_ptr<const Stream<TypeMatrix>> stream)
|
||||
: StreamRendererBase(ctx), m_stream(stream) { }
|
||||
|
||||
bool initialize() override;
|
||||
|
||||
bool spool(const CTime startTime, const CTime endTime) override { return spoolImpl<TypeMatrix, StreamRendererMatrix>(m_stream, *this, startTime, endTime); }
|
||||
|
||||
CString renderAsText(const size_t indent) const override;
|
||||
bool showChunkList() override;
|
||||
|
||||
protected:
|
||||
|
||||
friend bool spoolImpl<TypeMatrix, StreamRendererMatrix>(std::shared_ptr<const Stream<TypeMatrix>> stream, StreamRendererMatrix& renderer, CTime startTime,
|
||||
CTime endTime);
|
||||
|
||||
bool finalize() override;
|
||||
bool reset(CTime startTime, CTime endTime) override;
|
||||
bool push(const TypeMatrix::Buffer& chunk, bool zeroInput = false);
|
||||
bool mouseButton(int x, int y, int button, int status) override;
|
||||
|
||||
bool preDraw() override;
|
||||
bool draw() override;
|
||||
|
||||
size_t m_nRows = 0;
|
||||
size_t m_nCols = 0;
|
||||
|
||||
std::shared_ptr<const Stream<TypeMatrix>> m_stream;
|
||||
GtkWidget* m_streamListWindow = nullptr;
|
||||
|
||||
std::vector<float> m_swap;
|
||||
|
||||
StreamRendererMatrix() = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StreamRendererBase.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererNothing
|
||||
* \brief Renders nothing, to avoid null ptrs when we don't want even a label rendered
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
* @ fixme this class inherits a lot of junk from the base class which is not initialized properly and
|
||||
* hence cannot be used. but if just the interface below is used, its alright. Can't make base class
|
||||
* private as we store the renderer as a pointer to the base.
|
||||
*
|
||||
*/
|
||||
class StreamRendererNothing final : public StreamRendererBase
|
||||
{
|
||||
public:
|
||||
explicit StreamRendererNothing(const Kernel::IKernelContext& ctx) : StreamRendererBase(ctx) { }
|
||||
|
||||
bool initialize() override { return true; }
|
||||
bool uninitialize() override { return true; }
|
||||
bool setTitle(const char* /*title*/) override { return true; }
|
||||
|
||||
bool spool(const CTime /*startTime*/, const CTime /*endTime*/) override { return true; }
|
||||
|
||||
bool setRulerVisibility(bool /*isVisible*/) override { return true; }
|
||||
|
||||
bool showChunkList() override { return true; }
|
||||
|
||||
CString renderAsText(const size_t indent) const override
|
||||
{
|
||||
return (std::string(indent, ' ') + "Stream not selected and hence has no renderer in current mode\n").c_str();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
bool reset(CTime /*startTime*/, CTime /*endTime*/) override { return true; }
|
||||
bool realize() override { return true; }
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+50
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StreamRendererBase.h"
|
||||
#include "TypeSignal.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererSignal
|
||||
* \brief Renderer for Signal streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class StreamRendererSignal final : public StreamRendererBase
|
||||
{
|
||||
public:
|
||||
StreamRendererSignal(const Kernel::IKernelContext& ctx, std::shared_ptr<const Stream<TypeSignal>> stream)
|
||||
: StreamRendererBase(ctx), m_stream(stream) { }
|
||||
|
||||
bool initialize() override;
|
||||
|
||||
bool spool(const CTime startTime, const CTime endTime) override { return spoolImpl<TypeSignal, StreamRendererSignal>(m_stream, *this, startTime, endTime); }
|
||||
|
||||
CString renderAsText(const size_t indent) const override;
|
||||
bool showChunkList() override;
|
||||
|
||||
protected:
|
||||
|
||||
friend bool spoolImpl<TypeSignal, StreamRendererSignal>(std::shared_ptr<const Stream<TypeSignal>> stream, StreamRendererSignal& renderer,
|
||||
CTime startTime, CTime endTime);
|
||||
|
||||
bool push(const TypeSignal::Buffer& chunk, bool zeroInput = false);
|
||||
bool reset(CTime startTime, CTime endTime) override;
|
||||
bool mouseButton(int x, int y, int button, int status) override;
|
||||
|
||||
size_t m_nChannel = 0;
|
||||
size_t m_samplesPerChunk = 0;
|
||||
|
||||
std::shared_ptr<const Stream<TypeSignal>> m_stream;
|
||||
GtkWidget* m_streamListWindow = nullptr;
|
||||
|
||||
StreamRendererSignal() = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+59
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StreamRendererBase.h"
|
||||
#include "TypeSpectrum.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererSpectrum
|
||||
* \brief Renderer for Spectrum streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class StreamRendererSpectrum final : public StreamRendererBase
|
||||
{
|
||||
public:
|
||||
StreamRendererSpectrum(const Kernel::IKernelContext& ctx, std::shared_ptr<const Stream<TypeSpectrum>> stream)
|
||||
: StreamRendererBase(ctx), m_stream(stream) { }
|
||||
|
||||
bool initialize() override;
|
||||
|
||||
bool spool(const CTime startTime, const CTime endTime) override
|
||||
{
|
||||
return spoolImpl<TypeSpectrum, StreamRendererSpectrum>(m_stream, *this, startTime, endTime);
|
||||
}
|
||||
|
||||
CString renderAsText(const size_t indent) const override;
|
||||
bool showChunkList() override;
|
||||
|
||||
protected:
|
||||
|
||||
friend bool spoolImpl<TypeSpectrum, StreamRendererSpectrum>(std::shared_ptr<const Stream<TypeSpectrum>> stream, StreamRendererSpectrum& renderer,
|
||||
CTime startTime, CTime endTime);
|
||||
|
||||
bool finalize() override;
|
||||
bool reset(CTime startTime, CTime endTime) override;
|
||||
bool push(const TypeSpectrum::Buffer& chunk, bool zeroInput = false);
|
||||
bool mouseButton(int x, int y, int button, int status) override;
|
||||
|
||||
bool preDraw() override;
|
||||
bool draw() override;
|
||||
|
||||
size_t m_nChannel = 0;
|
||||
size_t m_spectrumElements = 0;
|
||||
|
||||
std::vector<float> m_swaps;
|
||||
|
||||
std::shared_ptr<const Stream<TypeSpectrum>> m_stream;
|
||||
GtkWidget* m_streamListWindow = nullptr;
|
||||
|
||||
StreamRendererSpectrum() = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "StreamRendererBase.h"
|
||||
#include "TypeStimulation.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class StreamRendererStimulation
|
||||
* \brief Renderer for Stimulation streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class StreamRendererStimulation final : public StreamRendererBase
|
||||
{
|
||||
public:
|
||||
StreamRendererStimulation(const Kernel::IKernelContext& ctx, std::shared_ptr<const Stream<TypeStimulation>> stream)
|
||||
: StreamRendererBase(ctx), m_stream(stream) { }
|
||||
|
||||
bool initialize() override;
|
||||
|
||||
bool spool(const CTime startTime, const CTime endTime) override
|
||||
{
|
||||
return spoolImpl<TypeStimulation, StreamRendererStimulation>(m_stream, *this, startTime, endTime);
|
||||
}
|
||||
|
||||
CString renderAsText(const size_t indent) const override;
|
||||
bool showChunkList() override;
|
||||
|
||||
protected:
|
||||
|
||||
friend bool spoolImpl<TypeStimulation, StreamRendererStimulation>(std::shared_ptr<const Stream<TypeStimulation>> stream,
|
||||
StreamRendererStimulation& renderer, CTime startTime, CTime endTime);
|
||||
|
||||
bool push(const TypeStimulation::Buffer& chunk, bool zeroInput = false);
|
||||
|
||||
bool reset(CTime startTime, CTime endTime) override;
|
||||
|
||||
bool mouseButton(int x, int y, int button, int status) override;
|
||||
|
||||
size_t m_nChannel = 0;
|
||||
size_t m_samplesPerChunk = 0;
|
||||
uint32_t m_sampling = 0;
|
||||
|
||||
GtkWidget* m_stimulationListWindow = nullptr;
|
||||
|
||||
std::shared_ptr<const Stream<TypeStimulation>> m_stream;
|
||||
|
||||
StreamRendererStimulation() = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TestClass
|
||||
* \brief Class to try out stuff in the Tracker
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TestClass
|
||||
{
|
||||
public:
|
||||
|
||||
explicit TestClass(Kernel::IKernelContext& ctx);
|
||||
|
||||
Kernel::IKernelContext& m_Ctx;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "ITrackerPlugin.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TrackerPluginChannelCheck
|
||||
* \brief Example of a Tracker plugin processing given Workspaces. It checks if all selected signal streams in all tracks have the same number of channels.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TrackerPluginChannelCheck final : public ITrackerPlugin
|
||||
{
|
||||
public:
|
||||
explicit TrackerPluginChannelCheck(const Kernel::IKernelContext& ctx) : ITrackerPlugin(ctx) { }
|
||||
bool process(Workspace& wp, ParallelExecutor& exec) override;
|
||||
bool hasCapability(const ECapabilities capability) override { return (capability == ECapabilities::Workspace); }
|
||||
std::string getName() override { return std::string("Example: Channel check"); }
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "ITrackerPlugin.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TrackerPluginCountStimulations
|
||||
* \brief Example of a Tracker plugin that processes given tracks. It counts the occurrences of different stimulations in each stimulation stream.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TrackerPluginCountStimulations final : public ITrackerPlugin
|
||||
{
|
||||
public:
|
||||
explicit TrackerPluginCountStimulations(const Kernel::IKernelContext& ctx) : ITrackerPlugin(ctx) { }
|
||||
bool process(StreamBundle& track) override;
|
||||
std::string getName() override { return std::string("Example: Count stimulations"); }
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+42
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "ITrackerPlugin.h"
|
||||
#include "Contexted.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TrackerPlugins
|
||||
* \author J. T. Lindgren
|
||||
* \brief Container class for currently registered Tracker plugins. These plugins get direct access to StreamBundles (i.e. full track content).
|
||||
* \details See ITrackerPlugin.h
|
||||
*
|
||||
New Tracker Plugins can be added to the Tracker by registering them in the constructor of this class
|
||||
*
|
||||
*/
|
||||
class TrackerPlugins final : protected Contexted
|
||||
{
|
||||
public:
|
||||
|
||||
explicit TrackerPlugins(const Kernel::IKernelContext& ctx);
|
||||
~TrackerPlugins() override;
|
||||
|
||||
// Get a list of all registered plugins
|
||||
const std::vector<ITrackerPlugin*>& getTrackerPlugins() const { return m_trackerPlugins; }
|
||||
|
||||
// Get a fresh plugin. The indexing is the same as in getTrackerPlugins() output
|
||||
ITrackerPlugin* getPluginCopy(size_t index) const;
|
||||
|
||||
protected:
|
||||
|
||||
// List of currently registered plugins
|
||||
std::vector<ITrackerPlugin*> m_trackerPlugins;
|
||||
|
||||
// List of calls to create new copies of the plugins
|
||||
std::vector<std::function<ITrackerPlugin*()>> m_pluginCreateCalls;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "TypeMatrix.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TypeChannelLocalization
|
||||
* \brief Abstact class defining chunk types for Channel Localization streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TypeChannelLocalization
|
||||
{
|
||||
public:
|
||||
static CIdentifier getTypeIdentifier() { return OV_TypeId_ChannelLocalisation; }
|
||||
|
||||
class Header : public TypeMatrix::Header
|
||||
{
|
||||
public:
|
||||
bool m_Dynamic = false;
|
||||
};
|
||||
|
||||
class Buffer : public TypeMatrix::Buffer { }; // Payload
|
||||
|
||||
class End : public TypeMatrix::End { };
|
||||
|
||||
// Prevent constructing
|
||||
TypeChannelLocalization() = delete;
|
||||
TypeChannelLocalization(const TypeChannelLocalization&) = delete;
|
||||
TypeChannelLocalization(TypeChannelLocalization&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "TypeMatrix.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TypeChannelUnits
|
||||
* \brief Abstact class defining chunk types for Channel Units streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TypeChannelUnits
|
||||
{
|
||||
public:
|
||||
static CIdentifier getTypeIdentifier() { return OV_TypeId_ChannelUnits; }
|
||||
|
||||
class Header : public TypeMatrix::Header
|
||||
{
|
||||
public:
|
||||
bool m_Dynamic = false;
|
||||
};
|
||||
|
||||
class Buffer : public TypeMatrix::Buffer { }; // Payload
|
||||
|
||||
class End : public TypeMatrix::End { };
|
||||
|
||||
// Prevent constructing
|
||||
TypeChannelUnits() = delete;
|
||||
TypeChannelUnits(const TypeChannelUnits&) = delete;
|
||||
TypeChannelUnits(TypeChannelUnits&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "Chunk.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TypeError
|
||||
* \brief Abstract class for error situations
|
||||
* \details
|
||||
*
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
|
||||
class TypeError
|
||||
{
|
||||
public:
|
||||
static CIdentifier getTypeIdentifier() { return CIdentifier::undefined(); }
|
||||
|
||||
class Header : public Chunk { };
|
||||
|
||||
class Buffer : public Chunk { };
|
||||
|
||||
class End : public Chunk { };
|
||||
|
||||
// Prevent constructing
|
||||
TypeError() = delete;
|
||||
TypeError(const TypeError&) = delete;
|
||||
TypeError(TypeError&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "Chunk.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TypeExperimentInfo
|
||||
* \brief Abstact class defining chunk types for Experiment Information streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TypeExperimentInfo
|
||||
{
|
||||
public:
|
||||
static CIdentifier getTypeIdentifier() { return OV_TypeId_ExperimentInfo; }
|
||||
|
||||
class Header : public Chunk
|
||||
{
|
||||
public:
|
||||
// Experiment
|
||||
uint64_t m_ExperimentID = 0;
|
||||
std::string m_ExperimentDate;
|
||||
|
||||
// Subject
|
||||
uint64_t m_SubjectID = 0;
|
||||
std::string m_SubjectName;
|
||||
uint64_t m_SubjectAge = 0;
|
||||
uint64_t m_SubjectGender = 0;
|
||||
|
||||
// Context
|
||||
uint64_t m_LaboratoryID = 0;
|
||||
std::string m_LaboratoryName;
|
||||
uint64_t m_TechnicianID = 0;
|
||||
std::string m_TechnicianName;
|
||||
};
|
||||
|
||||
class Buffer : public Chunk { }; // Payload
|
||||
|
||||
class End : public Chunk { };
|
||||
|
||||
// Prevent constructing
|
||||
TypeExperimentInfo() = delete;
|
||||
TypeExperimentInfo(const TypeExperimentInfo&) = delete;
|
||||
TypeExperimentInfo(TypeExperimentInfo&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "TypeMatrix.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TypeFeatureVector
|
||||
* \brief Abstact class defining chunk types for Feature Vector streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TypeFeatureVector
|
||||
{
|
||||
public:
|
||||
static CIdentifier getTypeIdentifier() { return OV_TypeId_FeatureVector; }
|
||||
|
||||
class Header : public TypeMatrix::Header { };
|
||||
|
||||
class Buffer : public TypeMatrix::Buffer { };
|
||||
|
||||
class End : public TypeMatrix::End { };
|
||||
|
||||
// Prevent constructing
|
||||
TypeFeatureVector() = delete;
|
||||
TypeFeatureVector(const TypeFeatureVector&) = delete;
|
||||
TypeFeatureVector(TypeFeatureVector&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "Chunk.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TypeMatrix
|
||||
* \brief Abstact class defining chunk types for Matrix streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TypeMatrix
|
||||
{
|
||||
public:
|
||||
static CIdentifier getTypeIdentifier() { return OV_TypeId_StreamedMatrix; }
|
||||
|
||||
class Header : public Chunk
|
||||
{
|
||||
public:
|
||||
// Header
|
||||
CMatrix m_Header;
|
||||
|
||||
// Make sure not copied until we have proper implementations
|
||||
Header() { }
|
||||
Header& operator=(const Header& in) = delete;
|
||||
Header(const Header& in) = delete;
|
||||
};
|
||||
|
||||
class Buffer : public Chunk
|
||||
{
|
||||
public:
|
||||
// Payload
|
||||
CMatrix m_buffer;
|
||||
|
||||
// Make sure not copied until we have proper implementations
|
||||
Buffer() { }
|
||||
Buffer& operator=(const Buffer& in) = delete;
|
||||
Buffer(const Buffer& in) = delete;
|
||||
};
|
||||
|
||||
class End : public Chunk { };
|
||||
|
||||
// Prevent constructing
|
||||
TypeMatrix() = delete;
|
||||
TypeMatrix(const TypeMatrix&) = delete;
|
||||
TypeMatrix(TypeMatrix&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "TypeMatrix.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TypeSignal
|
||||
* \brief Abstact class defining chunk types for Signal streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TypeSignal
|
||||
{
|
||||
public:
|
||||
static CIdentifier getTypeIdentifier() { return OV_TypeId_Signal; }
|
||||
|
||||
class Header : public TypeMatrix::Header
|
||||
{
|
||||
public:
|
||||
// Header
|
||||
uint64_t m_Sampling = 0;
|
||||
};
|
||||
|
||||
class Buffer : public TypeMatrix::Buffer { };
|
||||
|
||||
class End : public TypeMatrix::End { };
|
||||
|
||||
// Prevent constructing
|
||||
TypeSignal() = delete;
|
||||
TypeSignal(const TypeSignal&) = delete;
|
||||
TypeSignal(TypeSignal&&) = delete;
|
||||
|
||||
// class Decoder : public Toolkit::TSignalDecoder<BoxAlgorithmProxy> { };
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "TypeMatrix.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TypeSpectrum
|
||||
* \brief Abstact class defining chunk types for Spectrum streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TypeSpectrum
|
||||
{
|
||||
public:
|
||||
static CIdentifier getTypeIdentifier() { return OV_TypeId_Spectrum; }
|
||||
|
||||
class Header : public TypeMatrix::Header
|
||||
{
|
||||
public:
|
||||
uint64_t m_Sampling = 0;
|
||||
CMatrix m_Abscissas;
|
||||
|
||||
// Make sure not copied until we have proper implementations
|
||||
Header() { }
|
||||
Header& operator=(const Header& in) = delete;
|
||||
Header(const Header& in) = delete;
|
||||
};
|
||||
|
||||
class Buffer : public TypeMatrix::Buffer { };
|
||||
|
||||
class End : public TypeMatrix::End { };
|
||||
|
||||
// Prevent constructing
|
||||
TypeSpectrum() = delete;
|
||||
TypeSpectrum(const TypeSpectrum&) = delete;
|
||||
TypeSpectrum(TypeSpectrum&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "Chunk.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class TypeStimulation
|
||||
* \brief Abstact class defining chunk types for Stimulation streams
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class TypeStimulation
|
||||
{
|
||||
public:
|
||||
static CIdentifier getTypeIdentifier() { return OV_TypeId_Stimulations; }
|
||||
|
||||
class Header : public Chunk { };
|
||||
|
||||
class Buffer : public Chunk
|
||||
{
|
||||
public:
|
||||
// Payload
|
||||
CStimulationSet m_buffer;
|
||||
|
||||
// Make sure not copied until we have proper implementations
|
||||
Buffer() { }
|
||||
Buffer& operator=(const Buffer& in) = delete;
|
||||
Buffer(const Buffer& in) = delete;
|
||||
};
|
||||
|
||||
class End : public Chunk { };
|
||||
|
||||
// Prevent constructing
|
||||
TypeStimulation() = delete;
|
||||
TypeStimulation(const TypeStimulation&) = delete;
|
||||
TypeStimulation(TypeStimulation&&) = delete;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+247
@@ -0,0 +1,247 @@
|
||||
#pragma once
|
||||
|
||||
#include "StreamBundle.h"
|
||||
|
||||
#include "ProcExternalProcessing.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include "WorkspaceNotes.h"
|
||||
#include "Selection.h"
|
||||
#include "ParallelExecutor.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class Workspace
|
||||
* \brief Workspace is a set of Tracks (StreamBundles) that contain the .ov file content that the user wants to work with
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
*/
|
||||
class Workspace final : protected Contexted
|
||||
{
|
||||
public:
|
||||
|
||||
explicit Workspace(const Kernel::IKernelContext& ctx)
|
||||
: Contexted(ctx), m_workspaceFile(""), m_workspacePath(""), m_processor(ctx), m_selection(ctx, m_tracks) { setUniqueWorkingPath(); }
|
||||
|
||||
~Workspace() override;
|
||||
|
||||
// Reset workspace to the state like it was after construction
|
||||
bool clear();
|
||||
|
||||
// Manipulating tracks and streams currently in the workspace
|
||||
bool addTrack(const char* filename); // Add an .ov file as a track
|
||||
|
||||
bool moveTrack(size_t sourceIdx, size_t targetIdx);
|
||||
bool moveStream(size_t sourceTrack, size_t sourceStream, size_t targetTrack, size_t targetStream);
|
||||
|
||||
bool clearTracks();
|
||||
bool removeTrack(size_t idx);
|
||||
bool removeStream(size_t track, size_t stream);
|
||||
|
||||
size_t getNumTracks() const { return m_tracks.size(); }
|
||||
|
||||
size_t getNumTracksOnPlaylist() const { return m_playlist.size(); }
|
||||
size_t getNumTracksDone() const { return m_tracksDone; }
|
||||
|
||||
bool reloadTrack(size_t index);
|
||||
|
||||
// Does not transfer pointer ownership
|
||||
StreamBundle* getTrack(const size_t index)
|
||||
{
|
||||
std::unique_lock<std::mutex> (m_om_Mutex);
|
||||
|
||||
if (index >= m_tracks.size()) { return nullptr; }
|
||||
return m_tracks[index];
|
||||
}
|
||||
|
||||
// Transfers ownership of the pointer. Returns the index inserted to.
|
||||
bool setTrack(const size_t index, StreamBundle* track)
|
||||
{
|
||||
std::unique_lock<std::mutex> (m_om_Mutex);
|
||||
if (index >= m_tracks.size()) { m_tracks.resize(index + 1, nullptr); }
|
||||
delete m_tracks[index];
|
||||
m_tracks[index] = track;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool play(const bool playFast);
|
||||
bool stop(bool stopProcessor = true);
|
||||
|
||||
// Push the selected part of the tracks to processor
|
||||
bool step();
|
||||
|
||||
// get selection. Since it returns a reference, to set notes, simply modify the ref'd obj content.
|
||||
Selection& getSelection() { return m_selection; }
|
||||
|
||||
// Max duration of all tracks in the workspace, in fixed point seconds
|
||||
CTime getMaxDuration() const;
|
||||
|
||||
CTime getProcessedTime() const;
|
||||
CTime getPlaylistDuration() const { return m_playlistDuration; }
|
||||
|
||||
// Set output for chunks
|
||||
// @todo refactor these to a getter/setter of a processor object?
|
||||
bool setProcessor(const char* scenarioXml);
|
||||
bool setProcessorFlags(bool noGUI, bool doSend, bool doReceive);
|
||||
bool getProcessorFlags(bool& noGUI, bool& doSend, bool& doReceive) const;
|
||||
const char* getProcessorFile() const { return m_processor.getFilename().c_str(); }
|
||||
bool configureProcessor(const char* filename) { return m_processor.configure(filename); }
|
||||
bool setProcessorPorts(uint32_t sendPort, uint32_t recvPort);
|
||||
bool getProcessorPorts(uint32_t& sendPort, uint32_t& recvPort) const { return m_processor.getProcessorPorts(sendPort, recvPort); }
|
||||
|
||||
bool hasProcessor() const
|
||||
{
|
||||
const char* fn = getProcessorFile();
|
||||
return (fn && fn[0] != 0);
|
||||
}
|
||||
|
||||
CString getProcessorArguments() const { return m_processorArguments; }
|
||||
|
||||
bool setProcessorArguments(const CString& args)
|
||||
{
|
||||
m_processorArguments = args;
|
||||
return true;
|
||||
}
|
||||
|
||||
CString getWorkingPath() const { return m_workspacePath; }
|
||||
|
||||
bool setWorkingPath(const CString& path)
|
||||
{
|
||||
if (path.length() == 0)
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Empty workspace path not allowed\n";
|
||||
return false;
|
||||
}
|
||||
m_workspacePath = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Creates a 'unique' working path and assigns it, but does not create the folder
|
||||
bool setUniqueWorkingPath();
|
||||
|
||||
CString getFilename() const { return m_workspaceFile; }
|
||||
|
||||
bool setFilename(const CString& filename)
|
||||
{
|
||||
if (filename.length() == 0)
|
||||
{
|
||||
// unset
|
||||
m_workspaceFile = CString("");
|
||||
return true;
|
||||
}
|
||||
return this->save(filename);
|
||||
}
|
||||
|
||||
// get workspace notes. Since it returns a reference, to set notes, simply modify the ref'd obj content.
|
||||
Kernel::IComment& getNotes() { return m_notes; }
|
||||
|
||||
// @todo make generic property getter/setter?
|
||||
bool getCatenateMode() const { return m_catenateMode; }
|
||||
|
||||
bool setCatenateMode(const bool newState)
|
||||
{
|
||||
m_catenateMode = newState;
|
||||
return true;
|
||||
}
|
||||
|
||||
// @fixme rework the setter to spool files to/from disk when mode changes
|
||||
bool getMemorySaveMode() const { return m_memorySaveMode; }
|
||||
bool setMemorySaveMode(bool active);
|
||||
|
||||
// Modify tracks in place?
|
||||
bool getInplaceMode() const { return m_inplaceMode; }
|
||||
|
||||
bool setInplaceMode(const bool newState)
|
||||
{
|
||||
m_inplaceMode = newState;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t getRevision() const { return m_revision; }
|
||||
//uint64_t getNumRevisions() const { return m_NumRevisions; }
|
||||
|
||||
// The processors can poll this now and then to find out if they should quit ahead of the file end
|
||||
bool isQuitRequested() const
|
||||
{
|
||||
std::unique_lock<std::mutex> m_Mutex;
|
||||
return m_pleaseQuit;
|
||||
}
|
||||
|
||||
std::mutex& getMutex() const { return m_Mutex; }
|
||||
|
||||
// Clear configuration tokens with a given prefix
|
||||
bool wipeConfigurationTokens(const std::string& prefix) const;
|
||||
|
||||
bool setParallelExecutor(ParallelExecutor* ptr)
|
||||
{
|
||||
m_executor = ptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Save and load workspace
|
||||
bool save(const CString& filename);
|
||||
bool load(const CString& filename);
|
||||
|
||||
bool incrementRevisionAndSave(const CString& filename);
|
||||
|
||||
// get sorted workspace specific tokens
|
||||
std::vector<std::pair<std::string, std::string>> getConfigurationTokens() const;
|
||||
|
||||
protected:
|
||||
|
||||
bool saveAll();
|
||||
|
||||
bool getNextTrack(size_t& nextTrack) const;
|
||||
bool assemblePlaylist();
|
||||
|
||||
bool spoolRecordingToDisk(size_t trackIndex);
|
||||
|
||||
bool playReceiveOnly();
|
||||
bool playCatenate();
|
||||
bool playNormal();
|
||||
|
||||
// Assemble user-specified and generated arguments to the processor
|
||||
std::string getProcessorArguments(size_t index);
|
||||
|
||||
// Generic mutex to protect access to certain variables from multiple threads
|
||||
mutable std::mutex m_Mutex;
|
||||
|
||||
std::vector<StreamBundle*> m_tracks;
|
||||
|
||||
bool m_playFast = false;
|
||||
bool m_catenateMode = false;
|
||||
bool m_inplaceMode = false;
|
||||
bool m_memorySaveMode = false;
|
||||
bool m_pleaseQuit = false;
|
||||
|
||||
uint32_t m_tracksDone = 0;
|
||||
|
||||
CString m_workspaceFile;
|
||||
CString m_workspacePath;
|
||||
|
||||
// Component to send and receive data to/from, actually just a store for settings
|
||||
ProcExternalProcessing m_processor;
|
||||
std::string m_processorFilename;
|
||||
CString m_processorArguments;
|
||||
|
||||
typedef std::pair<StreamBundle*, CTime> SourceTimePair;
|
||||
std::vector<SourceTimePair> m_playlist;
|
||||
CTime m_playlistDuration = CTime::min();
|
||||
|
||||
// Text notes about the current workspace; defined as a class to be able to reuse Designer's CCommentEditorDialog
|
||||
WorkspaceNotes m_notes;
|
||||
|
||||
// Which tracks/streams are currently selected to be processed?
|
||||
Selection m_selection;
|
||||
|
||||
ParallelExecutor* m_executor = nullptr;
|
||||
|
||||
// uint64_t m_NumRevisions = 1;
|
||||
uint64_t m_revision = 1;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+89
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include <fs/Files.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
/**
|
||||
* \class WorkspaceNotes
|
||||
* \brief An overly complicated wrapper for a text string.
|
||||
* \author J. T. Lindgren
|
||||
*
|
||||
* This class is needed in order to use CCommentEditorDialog from Designer. The
|
||||
* implementation is a quick hack. IDs and Attributable features are not supported at all.
|
||||
*
|
||||
*/
|
||||
class WorkspaceNotes final : public Kernel::IComment
|
||||
{
|
||||
public:
|
||||
CIdentifier getIdentifier() const override { return 0; }
|
||||
CString getText() const override { return m_notes; }
|
||||
|
||||
bool setIdentifier(const CIdentifier& /*identifier*/) override { return true; }
|
||||
|
||||
bool setText(const CString& sText) override
|
||||
{
|
||||
m_notes = sText;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool initializeFromExistingComment(const IComment& exisitingComment) override
|
||||
{
|
||||
m_notes = exisitingComment.getText();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Attributable
|
||||
bool addAttribute(const CIdentifier& /*attributeID*/, const CString& /*sAttributeValue*/) override { return true; }
|
||||
bool removeAttribute(const CIdentifier& /*attributeID*/) override { return true; }
|
||||
bool removeAllAttributes() override { return true; }
|
||||
CString getAttributeValue(const CIdentifier& /*attributeID*/) const override { return CString(""); }
|
||||
bool setAttributeValue(const CIdentifier& /*attributeID*/, const CString& /*sAttributeValue*/) override { return true; }
|
||||
bool hasAttribute(const CIdentifier& /*attributeID*/) const override { return false; }
|
||||
bool hasAttributes() const override { return false; }
|
||||
CIdentifier getNextAttributeIdentifier(const CIdentifier& /*previousID*/) const override { return CIdentifier(); }
|
||||
CIdentifier getClassIdentifier() const override { return CIdentifier(); }
|
||||
|
||||
// Methods specific to this derived class
|
||||
|
||||
// Save the notes
|
||||
bool save(const CString& notesFile) const
|
||||
{
|
||||
std::ofstream output;
|
||||
FS::Files::openOFStream(output, notesFile.toASCIIString());
|
||||
if (!output.is_open() || output.bad()) { return false; }
|
||||
output << m_notes.toASCIIString();
|
||||
|
||||
output.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load the notes
|
||||
bool load(const CString& notesFile)
|
||||
{
|
||||
if (FS::Files::fileExists(notesFile.toASCIIString()))
|
||||
{
|
||||
std::ifstream input;
|
||||
FS::Files::openIFStream(input, notesFile.toASCIIString());
|
||||
if (input.is_open() && !input.bad())
|
||||
{
|
||||
const std::string str((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());
|
||||
m_notes = str.c_str();
|
||||
input.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
CString m_notes;
|
||||
};
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+933
@@ -0,0 +1,933 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<requires lib="gtk+" version="2.18"/>
|
||||
<!-- interface-naming-policy project-wide -->
|
||||
<object class="GtkAdjustment" id="adjustment_data_scale">
|
||||
<property name="upper">100000</property>
|
||||
<property name="value">1</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment_element_count">
|
||||
<property name="lower">2</property>
|
||||
<property name="upper">1024</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">25</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment_erp">
|
||||
<property name="upper">1</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment_freq_band_max">
|
||||
<property name="upper">8192</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment_freq_band_min">
|
||||
<property name="upper">8192</property>
|
||||
<property name="step_increment">1</property>
|
||||
<property name="page_increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="adjustment_time_scale">
|
||||
<property name="upper">10000000</property>
|
||||
<property name="value">10</property>
|
||||
<property name="step_increment">0.10000000000000001</property>
|
||||
<property name="page_increment">1</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="expander_sort_image_alpha">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-spell-check</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="expander_sort_image_alpha1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-spell-check</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="expander_sort_image_default">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-close</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="expander_sort_image_default1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-close</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="expander_sort_image_front_back">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-go-down</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="expander_sort_image_front_back1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-go-down</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="expander_sort_image_left_right">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-go-forward</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="expander_sort_image_left_right1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-go-forward</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="expander_sort_image_reversed">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-undo</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="expander_sort_image_reversed1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-undo</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="liststore_select">
|
||||
<columns>
|
||||
<!-- column-name index -->
|
||||
<column type="guint"/>
|
||||
<!-- column-name name -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0">1</col>
|
||||
<col id="1" translatable="yes">dummy</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkListStore" id="liststore_select_broadmann">
|
||||
<columns>
|
||||
<!-- column-name name -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">dummy</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkListStore" id="liststore_select_neuro_1">
|
||||
<columns>
|
||||
<!-- column-name name -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">dummy</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkListStore" id="liststore_select_neuro_2">
|
||||
<columns>
|
||||
<!-- column-name name -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">dummy</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkListStore" id="liststore_sort">
|
||||
<columns>
|
||||
<!-- column-name text -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name icon -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
<data>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Sort Channels...</col>
|
||||
<col id="1" translatable="yes"></col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Default</col>
|
||||
<col id="1" translatable="yes">gtk-close</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Alphabetically</col>
|
||||
<col id="1" translatable="yes">gtk-spell-check</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Reversed</col>
|
||||
<col id="1" translatable="yes">gtk-undo</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Left to Right</col>
|
||||
<col id="1" translatable="yes">gtk-go-back</col>
|
||||
</row>
|
||||
<row>
|
||||
<col id="0" translatable="yes">Front to Bottom</col>
|
||||
<col id="1" translatable="yes">gtk-go-down</col>
|
||||
</row>
|
||||
</data>
|
||||
</object>
|
||||
<object class="GtkWindow" id="toolbar-window">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Configure Settings...</property>
|
||||
<property name="window_position">mouse</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">4</property>
|
||||
<property name="spacing">4</property>
|
||||
<child>
|
||||
<object class="GtkExpander" id="expander_sort">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<child>
|
||||
<object class="GtkTable" id="table_sort">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="n_columns">5</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="expander_sort_button_default">
|
||||
<property name="label" translatable="yes">Default</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="image">expander_sort_image_default</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="expander_sort_button_alpha">
|
||||
<property name="label" translatable="yes">Alphabetically</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="image">expander_sort_image_alpha</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="expander_sort_button_reversed">
|
||||
<property name="label" translatable="yes">Reversed</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="image">expander_sort_image_reversed</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="right_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="expander_sort_button_left_right">
|
||||
<property name="label" translatable="yes">Left to Right</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="image">expander_sort_image_left_right</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">3</property>
|
||||
<property name="right_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="expander_sort_button_front_back">
|
||||
<property name="label" translatable="yes">Front to Back</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="image">expander_sort_image_front_back</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">4</property>
|
||||
<property name="right_attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="expander_sort_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Sort Channels...</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkExpander" id="expander_select">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow_select">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">automatic</property>
|
||||
<property name="vscrollbar_policy">automatic</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="expander_select_treeview">
|
||||
<property name="height_request">240</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">liststore_select</property>
|
||||
<property name="rules_hint">True</property>
|
||||
<property name="search_column">1</property>
|
||||
<property name="fixed_height_mode">True</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="treeviewcolumn_channel">
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="title" translatable="yes">Channel</property>
|
||||
<property name="clickable">True</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext_channel_index">
|
||||
<property name="width">32</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext_channel_name"/>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="expander_select_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Select Channels...</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkExpander" id="expander_select_sLORETA">
|
||||
<property name="can_focus">True</property>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox_select_region">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">4</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow_select_broadmann">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">automatic</property>
|
||||
<property name="vscrollbar_policy">automatic</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="expander_select_broadmann_treeview">
|
||||
<property name="height_request">240</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">liststore_select_broadmann</property>
|
||||
<property name="rules_hint">True</property>
|
||||
<property name="search_column">1</property>
|
||||
<property name="fixed_height_mode">True</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="treeviewcolumn_broadmann">
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="title" translatable="yes">Broadmann Area</property>
|
||||
<property name="clickable">True</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext_broadmann_name"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow_select_neuro_1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">automatic</property>
|
||||
<property name="vscrollbar_policy">automatic</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="expander_select_neuro_1_treeview">
|
||||
<property name="height_request">240</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">liststore_select_neuro_1</property>
|
||||
<property name="rules_hint">True</property>
|
||||
<property name="search_column">1</property>
|
||||
<property name="fixed_height_mode">True</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="treeviewcolumn_neuro_1">
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="title" translatable="yes">Neuro Anatomical Region 1</property>
|
||||
<property name="clickable">True</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext_neuro_1_name"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolledwindow_select_neuro_2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">automatic</property>
|
||||
<property name="vscrollbar_policy">automatic</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="expander_select_neuro_2_treeview">
|
||||
<property name="height_request">240</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">liststore_select_neuro_2</property>
|
||||
<property name="rules_hint">True</property>
|
||||
<property name="search_column">1</property>
|
||||
<property name="fixed_height_mode">True</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="treeviewcolumn_neuro_2">
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="title" translatable="yes">Neuro Anatomical Region 2</property>
|
||||
<property name="clickable">True</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="cellrenderertext_neuro_2_name"/>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="expander_select_region_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Select Regions...</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkExpander" id="expander_freq_band">
|
||||
<property name="can_focus">True</property>
|
||||
<child>
|
||||
<object class="GtkTable" id="table_freq_band">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="n_rows">2</property>
|
||||
<property name="n_columns">2</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_freq_band_min">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Lower Boundary :</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_freq_band_max">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Higher Boundary :</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_freq_band_min">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="invisible_char_set">True</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="primary_icon_sensitive">True</property>
|
||||
<property name="secondary_icon_sensitive">True</property>
|
||||
<property name="adjustment">adjustment_freq_band_min</property>
|
||||
<property name="digits">3</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_freq_band_max">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="invisible_char_set">True</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="primary_icon_sensitive">True</property>
|
||||
<property name="secondary_icon_sensitive">True</property>
|
||||
<property name="adjustment">adjustment_freq_band_max</property>
|
||||
<property name="digits">3</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="label_freq_band">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Select Spectrum Boundaries...</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTable" id="table_scales">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="n_rows">2</property>
|
||||
<property name="n_columns">3</property>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox_time_scale">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_time_scale">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Time Scale :</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_time_scale">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="invisible_char_set">True</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="primary_icon_sensitive">True</property>
|
||||
<property name="secondary_icon_sensitive">True</property>
|
||||
<property name="adjustment">adjustment_time_scale</property>
|
||||
<property name="digits">3</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="checkbutton_show_scale">
|
||||
<property name="label" translatable="yes">Show Scales</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="checkbutton_positive">
|
||||
<property name="label" translatable="yes">Positive Data</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox_element_count">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_element_count">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Element Count :</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="spinbutton_element_count">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="invisible_char_set">True</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="primary_icon_sensitive">True</property>
|
||||
<property name="secondary_icon_sensitive">True</property>
|
||||
<property name="adjustment">adjustment_element_count</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHSeparator" id="hseparator1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTable" id="table_erp">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="n_columns">4</property>
|
||||
<property name="column_spacing">4</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="button_erp_play_pause">
|
||||
<property name="label">gtk-media-play</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">3</property>
|
||||
<property name="right_attach">4</property>
|
||||
<property name="x_options"></property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_erp_progress">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">0%</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHScale" id="range_erp">
|
||||
<property name="width_request">250</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">adjustment_erp</property>
|
||||
<property name="digits">3</property>
|
||||
<property name="draw_value">False</property>
|
||||
<property name="value_pos">right</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_erp_title">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">ERP player :</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox_video_recording">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">4</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_video_recording_title">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Video will save to :</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_video_recording_filename">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button_video_recording">
|
||||
<property name="label">gtk-media-record</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkWindow" id="window">
|
||||
<property name="width_request">640</property>
|
||||
<property name="height_request">480</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Test window</property>
|
||||
<child>
|
||||
<object class="GtkTable" id="table">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="n_rows">3</property>
|
||||
<property name="n_columns">3</property>
|
||||
<child>
|
||||
<object class="GtkDrawingArea" id="drawingarea_bottom">
|
||||
<property name="height_request">32</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_corner_left">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="x_options"></property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_corner_right">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="x_options"></property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkDrawingArea" id="drawingarea_right">
|
||||
<property name="width_request">64</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkDrawingArea" id="drawingarea_left">
|
||||
<property name="width_request">64</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label_top">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="x_options"></property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkDrawingArea" id="viewport">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_SUBSTRUCTURE_MASK</property>
|
||||
<property name="double_buffered">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
Executable
+858
@@ -0,0 +1,858 @@
|
||||
<OpenViBE-Scenario>
|
||||
<FormatVersion>2</FormatVersion>
|
||||
<Creator>OpenViBE Designer</Creator>
|
||||
<CreatorVersion>2.1.0</CreatorVersion>
|
||||
<Settings></Settings>
|
||||
<Inputs></Inputs>
|
||||
<Outputs></Outputs>
|
||||
<Boxes>
|
||||
<Box>
|
||||
<Identifier>(0x000013ae, 0x000053d6)</Identifier>
|
||||
<Name>Temporal Filter</Name>
|
||||
<AlgorithmClassIdentifier>(0xb4f9d042, 0x9d79f2e5)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Input signal</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Output signal</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2f2c606c, 0x8512ed68)</TypeIdentifier>
|
||||
<Name>Filter Method</Name>
|
||||
<DefaultValue>Butterworth</DefaultValue>
|
||||
<Value>Butterworth</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0xfa20178e, 0x4cba62e9)</TypeIdentifier>
|
||||
<Name>Filter Type</Name>
|
||||
<DefaultValue>Band Pass</DefaultValue>
|
||||
<Value>Band Pass</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Filter Order</Name>
|
||||
<DefaultValue>4</DefaultValue>
|
||||
<Value>4</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Low Cut-off Frequency (Hz)</Name>
|
||||
<DefaultValue>1</DefaultValue>
|
||||
<Value>1</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>High Cut-off Frequency (Hz)</Name>
|
||||
<DefaultValue>40</DefaultValue>
|
||||
<Value>2</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Band Pass Ripple (dB)</Name>
|
||||
<DefaultValue>0.5</DefaultValue>
|
||||
<Value>0.5</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>112</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>288</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x27a4ceec, 0x876d6384)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>6</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x000020f0, 0x00006a2e)</Identifier>
|
||||
<Name>Player Controller</Name>
|
||||
<AlgorithmClassIdentifier>(0x5f426dce, 0x08456e13)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Stimulations</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
|
||||
<Name>Stimulation name</Name>
|
||||
<DefaultValue>OVTK_StimulationId_Label_00</DefaultValue>
|
||||
<Value>OVTK_StimulationId_Label_00</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0xcc14d8d6, 0xf27ecb73)</TypeIdentifier>
|
||||
<Name>Action to perform</Name>
|
||||
<DefaultValue>Pause</DefaultValue>
|
||||
<Value>Stop</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>336</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>480</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x568d148e, 0x650792b3)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x000039e9, 0x000043de)</Identifier>
|
||||
<Name>Timeout</Name>
|
||||
<AlgorithmClassIdentifier>(0x24fcd292, 0x5c8f6aa8)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x544a003e, 0x6dcba5f6)</TypeIdentifier>
|
||||
<Name>Input Stream</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Output Stimulations</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Timeout delay</Name>
|
||||
<DefaultValue>5</DefaultValue>
|
||||
<Value>5</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
|
||||
<Name>Output Stimulation</Name>
|
||||
<DefaultValue>OVTK_StimulationId_Label_00</DefaultValue>
|
||||
<Value>OVTK_StimulationId_Label_00</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>272</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>480</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x1eaee00e, 0xdb05d34e)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x000043a8, 0x000051c6)</Identifier>
|
||||
<Name>Debug: Stims</Name>
|
||||
<AlgorithmClassIdentifier>(0x65731e1d, 0x47de5276)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Stimulation stream 1</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0xa88b3667, 0x0871638c)</TypeIdentifier>
|
||||
<Name>Log level to use</Name>
|
||||
<DefaultValue>Information</DefaultValue>
|
||||
<Value>Information</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>112</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>608</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x341d3912, 0x1478de86)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0xf451ad91, 0x14c75f86)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00005438, 0x0000400f)</Identifier>
|
||||
<Name>To Tracker</Name>
|
||||
<AlgorithmClassIdentifier>(0x15422959, 0x16304449)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>New input</Name>
|
||||
</Input>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>New input(1)</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Launch third party program</Name>
|
||||
<DefaultValue>true</DefaultValue>
|
||||
<Value>false</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
|
||||
<Name>Executable path</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value></Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
|
||||
<Name>Arguments</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value></Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Port</Name>
|
||||
<DefaultValue>0</DefaultValue>
|
||||
<Value>${Tracker_Port_Receive}</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Automatic connection identifier</Name>
|
||||
<DefaultValue>true</DefaultValue>
|
||||
<Value>true</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
|
||||
<Name>Custom connection identifier</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value></Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Incoming connection timeout</Name>
|
||||
<DefaultValue>10</DefaultValue>
|
||||
<Value>10</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Generator</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>false</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x17ee7c08, 0x94c14893)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>192</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>336</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x30a4e5c9, 0x83502953)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x6410533a, 0x11ff9fa8)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x61d11811, 0x71e65362)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>8</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf191c1c8, 0xa0123976)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00005438, 0x00004010)</Identifier>
|
||||
<Name>From Tracker</Name>
|
||||
<AlgorithmClassIdentifier>(0x15422959, 0x16304449)</AlgorithmClassIdentifier>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>New output</Name>
|
||||
</Output>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>New output(1)</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Launch third party program</Name>
|
||||
<DefaultValue>true</DefaultValue>
|
||||
<Value>false</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
|
||||
<Name>Executable path</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value></Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
|
||||
<Name>Arguments</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value></Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Port</Name>
|
||||
<DefaultValue>0</DefaultValue>
|
||||
<Value>${Tracker_Port_Send}</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Automatic connection identifier</Name>
|
||||
<DefaultValue>true</DefaultValue>
|
||||
<Value>true</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
|
||||
<Name>Custom connection identifier</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value></Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Incoming connection timeout</Name>
|
||||
<DefaultValue>10</DefaultValue>
|
||||
<Value>10</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Generator</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>true</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x17ee7c08, 0x94c14893)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>16</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>304</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x30a4e5c9, 0x83502953)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x6410533a, 0x11ff9fa8)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x61d11811, 0x71e65362)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>8</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf191c1c8, 0xa0123976)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00005c5f, 0x0000103e)</Identifier>
|
||||
<Name>Filtered signal</Name>
|
||||
<AlgorithmClassIdentifier>(0x0055be5f, 0x087bdd12)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Data</Name>
|
||||
</Input>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Stimulations</Name>
|
||||
</Input>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6ab26b81, 0x0f8c02f3)</TypeIdentifier>
|
||||
<Name>Channel Units</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x5de046a6, 0x086340aa)</TypeIdentifier>
|
||||
<Name>Display Mode</Name>
|
||||
<DefaultValue>Scan</DefaultValue>
|
||||
<Value>Scan</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x33a30739, 0x00d5299b)</TypeIdentifier>
|
||||
<Name>Auto vertical scale</Name>
|
||||
<DefaultValue>Per channel</DefaultValue>
|
||||
<Value>Global</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Scale refresh interval (secs)</Name>
|
||||
<DefaultValue>5</DefaultValue>
|
||||
<Value>5</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Vertical Scale</Name>
|
||||
<DefaultValue>100</DefaultValue>
|
||||
<Value>2</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Vertical Offset</Name>
|
||||
<DefaultValue>0</DefaultValue>
|
||||
<Value>0</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Time Scale</Name>
|
||||
<DefaultValue>10</DefaultValue>
|
||||
<Value>10</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Bottom ruler</Name>
|
||||
<DefaultValue>true</DefaultValue>
|
||||
<Value>true</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Left ruler</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>false</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Multiview</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>false</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>192</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>192</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x92c056a7, 0x2dc71aff)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>9</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>3</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00005dd2, 0x00007417)</Identifier>
|
||||
<Name>Debug: Signals</Name>
|
||||
<AlgorithmClassIdentifier>(0x0ed76695, 0x01a69cc3)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x434f6587, 0x2efd2b7e)</TypeIdentifier>
|
||||
<Name>Spied EBML stream 1</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
|
||||
<Name>EBML nodes description</Name>
|
||||
<DefaultValue>${Path_Data}/plugins/tools/config-ebml-stream-spy.txt</DefaultValue>
|
||||
<Value>${Path_Data}/plugins/tools/config-ebml-stream-spy.txt</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0xa88b3667, 0x0871638c)</TypeIdentifier>
|
||||
<Name>Log level to use</Name>
|
||||
<DefaultValue>Information</DefaultValue>
|
||||
<Value>Information</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Expand binary blocks</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>true</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Number of values in expanded blocks</Name>
|
||||
<DefaultValue>4</DefaultValue>
|
||||
<Value>4</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>112</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>480</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x341d3912, 0x1478de86)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x23ce0a10, 0xc2eabb47)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>4</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
</Boxes>
|
||||
<Links>
|
||||
<Link>
|
||||
<Identifier>(0x000013b9, 0x0000017a)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x000039e9, 0x000043de)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x000020f0, 0x00006a2e)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00002203, 0x000031ea)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00005438, 0x00004010)</BoxIdentifier>
|
||||
<BoxOutputIndex>1</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x000043a8, 0x000051c6)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00003091, 0x000011d5)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00005438, 0x00004010)</BoxIdentifier>
|
||||
<BoxOutputIndex>1</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00005c5f, 0x0000103e)</BoxIdentifier>
|
||||
<BoxInputIndex>1</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x000042ec, 0x0000374f)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x000013ae, 0x000053d6)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00005438, 0x0000400f)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x000054e6, 0x00000d08)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00005438, 0x00004010)</BoxIdentifier>
|
||||
<BoxOutputIndex>1</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00005438, 0x0000400f)</BoxIdentifier>
|
||||
<BoxInputIndex>1</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x000057ce, 0x00000c0b)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00005438, 0x00004010)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x000039e9, 0x000043de)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00005a57, 0x00005592)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00005438, 0x00004010)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x000013ae, 0x000053d6)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00005bab, 0x000064b9)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00005438, 0x00004010)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00005dd2, 0x00007417)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00007a40, 0x00004a8b)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x000013ae, 0x000053d6)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00005c5f, 0x0000103e)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
</Links>
|
||||
<Comments>
|
||||
<Comment>
|
||||
<Identifier>(0x000021fb, 0x00001fe1)</Identifier>
|
||||
<Text>This scenario illustrates receiving a signal
|
||||
from the Tracker, filtering and displaying it,
|
||||
and sending the filtered signal back
|
||||
to the Tracker.
|
||||
|
||||
The filtered result should appear as a
|
||||
new track in the Tracker workspace.</Text>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x473d9a43, 0x97fc0a97)</Identifier>
|
||||
<Value>-16</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x7234b86b, 0x2b8651a5)</Identifier>
|
||||
<Value>64</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Comment>
|
||||
<Comment>
|
||||
<Identifier>(0x000021fb, 0x00001fe2)</Identifier>
|
||||
<Text>It is important that the Tracker workspace
|
||||
is configured to both send and receive when
|
||||
this scenario is used as its processor.
|
||||
|
||||
Note that the number of output streams and their
|
||||
order in the 'From Tracker' box must exactly
|
||||
match the selected streams in the Tracker.</Text>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x473d9a43, 0x97fc0a97)</Identifier>
|
||||
<Value>-16</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x7234b86b, 0x2b8651a5)</Identifier>
|
||||
<Value>256</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Comment>
|
||||
</Comments>
|
||||
<Metadata>
|
||||
<Entry>
|
||||
<Identifier>(0x000000c2, 0x000011b4)</Identifier>
|
||||
<Type>(0x3bcce5d2, 0x43f2d968)</Type>
|
||||
<Data>[{"boxIdentifier":"(0x00005c5f, 0x0000103e)","childCount":0,"identifier":"(0x00001f6f, 0x000016eb)","parentIdentifier":"(0xffffffff, 0xffffffff)","type":3},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"height":320,"identifier":"(0x00003cf2, 0x00002d05)","name":"Default window","parentIdentifier":"(0xffffffff, 0xffffffff)","type":1,"width":480},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"identifier":"(0x00007351, 0x00002b8e)","index":0,"name":"Default tab","parentIdentifier":"(0x00003cf2, 0x00002d05)","type":2},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":0,"identifier":"(0x0000355e, 0x00004491)","index":0,"name":"Empty","parentIdentifier":"(0x00007351, 0x00002b8e)","type":0}]</Data>
|
||||
</Entry>
|
||||
</Metadata>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x079d1f09, 0x667c7871)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x60bc58ab, 0x7a1b4d83)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x790d75b8, 0x3bb90c33)</Identifier>
|
||||
<Value>Jussi T. Lindgren</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x7a3a1558, 0xf12c63c2)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x84009d7c, 0x3c4e763b)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x8c1fc55b, 0x7b433dc2)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x9f5c4075, 0x4a0d3666)</Identifier>
|
||||
<Value>Tracker bidir example</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf36a1567, 0xd13c53da)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf6b2e3fa, 0x7bd43926)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf8034a49, 0x8b3f37cc)</Identifier>
|
||||
<Value>Inria</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</OpenViBE-Scenario>
|
||||
Executable
+1207
File diff suppressed because it is too large
Load Diff
Executable
+701
@@ -0,0 +1,701 @@
|
||||
<OpenViBE-Scenario>
|
||||
<FormatVersion>2</FormatVersion>
|
||||
<Creator>OpenViBE Designer</Creator>
|
||||
<CreatorVersion>2.1.0</CreatorVersion>
|
||||
<Settings></Settings>
|
||||
<Inputs></Inputs>
|
||||
<Outputs></Outputs>
|
||||
<Boxes>
|
||||
<Box>
|
||||
<Identifier>(0x000013ae, 0x000053d6)</Identifier>
|
||||
<Name>Temporal Filter</Name>
|
||||
<AlgorithmClassIdentifier>(0xb4f9d042, 0x9d79f2e5)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Input signal</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Output signal</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2f2c606c, 0x8512ed68)</TypeIdentifier>
|
||||
<Name>Filter Method</Name>
|
||||
<DefaultValue>Butterworth</DefaultValue>
|
||||
<Value>Butterworth</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0xfa20178e, 0x4cba62e9)</TypeIdentifier>
|
||||
<Name>Filter Type</Name>
|
||||
<DefaultValue>Band Pass</DefaultValue>
|
||||
<Value>Band Pass</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Filter Order</Name>
|
||||
<DefaultValue>4</DefaultValue>
|
||||
<Value>4</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Low Cut-off Frequency (Hz)</Name>
|
||||
<DefaultValue>1</DefaultValue>
|
||||
<Value>1</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>High Cut-off Frequency (Hz)</Name>
|
||||
<DefaultValue>40</DefaultValue>
|
||||
<Value>40</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Band Pass Ripple (dB)</Name>
|
||||
<DefaultValue>0.5</DefaultValue>
|
||||
<Value>0.5</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>128</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>144</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x27a4ceec, 0x876d6384)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>6</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x000020f0, 0x00006a2e)</Identifier>
|
||||
<Name>Player Controller</Name>
|
||||
<AlgorithmClassIdentifier>(0x5f426dce, 0x08456e13)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Stimulations</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
|
||||
<Name>Stimulation name</Name>
|
||||
<DefaultValue>OVTK_StimulationId_Label_00</DefaultValue>
|
||||
<Value>OVTK_StimulationId_Label_00</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0xcc14d8d6, 0xf27ecb73)</TypeIdentifier>
|
||||
<Name>Action to perform</Name>
|
||||
<DefaultValue>Pause</DefaultValue>
|
||||
<Value>Stop</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>240</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>528</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x568d148e, 0x650792b3)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x000043a8, 0x000051c6)</Identifier>
|
||||
<Name>Stims</Name>
|
||||
<AlgorithmClassIdentifier>(0x65731e1d, 0x47de5276)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Stimulation stream 1</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0xa88b3667, 0x0871638c)</TypeIdentifier>
|
||||
<Name>Log level to use</Name>
|
||||
<DefaultValue>Information</DefaultValue>
|
||||
<Value>Information</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>224</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>352</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x341d3912, 0x1478de86)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0xf451ad91, 0x14c75f86)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00005438, 0x0000400f)</Identifier>
|
||||
<Name>External Processing</Name>
|
||||
<AlgorithmClassIdentifier>(0x15422959, 0x16304449)</AlgorithmClassIdentifier>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>New output</Name>
|
||||
</Output>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>New output(1)</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Launch third party program</Name>
|
||||
<DefaultValue>true</DefaultValue>
|
||||
<Value>false</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
|
||||
<Name>Executable path</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value></Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
|
||||
<Name>Arguments</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value></Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Port</Name>
|
||||
<DefaultValue>0</DefaultValue>
|
||||
<Value>${Tracker_Port_Send}</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Automatic connection identifier</Name>
|
||||
<DefaultValue>true</DefaultValue>
|
||||
<Value>true</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
|
||||
<Name>Custom connection identifier</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value></Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Incoming connection timeout</Name>
|
||||
<DefaultValue>10</DefaultValue>
|
||||
<Value>10</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Generator</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>true</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x17ee7c08, 0x94c14893)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>32</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>288</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x30a4e5c9, 0x83502953)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x6410533a, 0x11ff9fa8)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x61d11811, 0x71e65362)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>8</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf191c1c8, 0xa0123976)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00005ae7, 0x00003098)</Identifier>
|
||||
<Name>Timeout</Name>
|
||||
<AlgorithmClassIdentifier>(0x24fcd292, 0x5c8f6aa8)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x544a003e, 0x6dcba5f6)</TypeIdentifier>
|
||||
<Name>Input Stream</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Output Stimulations</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Timeout delay</Name>
|
||||
<DefaultValue>5</DefaultValue>
|
||||
<Value>3</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
|
||||
<Name>Output Stimulation</Name>
|
||||
<DefaultValue>OVTK_StimulationId_Label_00</DefaultValue>
|
||||
<Value>OVTK_StimulationId_Label_00</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>176</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>512</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x1eaee00e, 0xdb05d34e)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00005c5f, 0x0000103e)</Identifier>
|
||||
<Name>Signal display</Name>
|
||||
<AlgorithmClassIdentifier>(0x0055be5f, 0x087bdd12)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Data</Name>
|
||||
</Input>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Stimulations</Name>
|
||||
</Input>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6ab26b81, 0x0f8c02f3)</TypeIdentifier>
|
||||
<Name>Channel Units</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x5de046a6, 0x086340aa)</TypeIdentifier>
|
||||
<Name>Display Mode</Name>
|
||||
<DefaultValue>Scan</DefaultValue>
|
||||
<Value>Scan</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x33a30739, 0x00d5299b)</TypeIdentifier>
|
||||
<Name>Auto vertical scale</Name>
|
||||
<DefaultValue>Per channel</DefaultValue>
|
||||
<Value>Global</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Scale refresh interval (secs)</Name>
|
||||
<DefaultValue>5</DefaultValue>
|
||||
<Value>5</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Vertical Scale</Name>
|
||||
<DefaultValue>100</DefaultValue>
|
||||
<Value>2</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Vertical Offset</Name>
|
||||
<DefaultValue>0</DefaultValue>
|
||||
<Value>0</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Time Scale</Name>
|
||||
<DefaultValue>10</DefaultValue>
|
||||
<Value>10</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Bottom ruler</Name>
|
||||
<DefaultValue>true</DefaultValue>
|
||||
<Value>true</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Left ruler</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>false</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Multiview</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>false</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>224</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>96</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x92c056a7, 0x2dc71aff)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>9</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>3</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00005dd2, 0x00007417)</Identifier>
|
||||
<Name>Signals</Name>
|
||||
<AlgorithmClassIdentifier>(0x0ed76695, 0x01a69cc3)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x434f6587, 0x2efd2b7e)</TypeIdentifier>
|
||||
<Name>Spied EBML stream 1</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
|
||||
<Name>EBML nodes description</Name>
|
||||
<DefaultValue>${Path_Data}/plugins/tools/config-ebml-stream-spy.txt</DefaultValue>
|
||||
<Value>${Path_Data}/plugins/tools/config-ebml-stream-spy.txt</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0xa88b3667, 0x0871638c)</TypeIdentifier>
|
||||
<Name>Log level to use</Name>
|
||||
<DefaultValue>Information</DefaultValue>
|
||||
<Value>Information</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Expand binary blocks</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>true</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Number of values in expanded blocks</Name>
|
||||
<DefaultValue>4</DefaultValue>
|
||||
<Value>4</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>224</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>224</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x341d3912, 0x1478de86)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x23ce0a10, 0xc2eabb47)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>4</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
</Boxes>
|
||||
<Links>
|
||||
<Link>
|
||||
<Identifier>(0x00002634, 0x000066c2)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00005438, 0x0000400f)</BoxIdentifier>
|
||||
<BoxOutputIndex>1</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00005dd2, 0x00007417)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00002895, 0x00007435)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00005438, 0x0000400f)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x000013ae, 0x000053d6)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00003d33, 0x00004bba)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00005438, 0x0000400f)</BoxIdentifier>
|
||||
<BoxOutputIndex>1</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00005c5f, 0x0000103e)</BoxIdentifier>
|
||||
<BoxInputIndex>1</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x000046c0, 0x00004d90)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00005438, 0x0000400f)</BoxIdentifier>
|
||||
<BoxOutputIndex>1</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x000043a8, 0x000051c6)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x000050d8, 0x000049e5)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00005438, 0x0000400f)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00005ae7, 0x00003098)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00007a40, 0x00004a8b)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x000013ae, 0x000053d6)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00005c5f, 0x0000103e)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00007aee, 0x00005c93)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00005ae7, 0x00003098)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x000020f0, 0x00006a2e)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
</Links>
|
||||
<Comments>
|
||||
<Comment>
|
||||
<Identifier>(0x00001912, 0x0000689c)</Identifier>
|
||||
<Text>Example of a processor that receives data from the Tracker,
|
||||
but does not send anything to it:
|
||||
the processor works as a <b>Data Sink</b>.
|
||||
|
||||
In this case, the tracker Workspace must be
|
||||
configured to Send but not to Receive.
|
||||
</Text>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x473d9a43, 0x97fc0a97)</Identifier>
|
||||
<Value>-144</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x7234b86b, 0x2b8651a5)</Identifier>
|
||||
<Value>160</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Comment>
|
||||
</Comments>
|
||||
<Metadata>
|
||||
<Entry>
|
||||
<Identifier>(0x000000c2, 0x000011b4)</Identifier>
|
||||
<Type>(0x3bcce5d2, 0x43f2d968)</Type>
|
||||
<Data>[{"boxIdentifier":"(0x00005c5f, 0x0000103e)","childCount":0,"identifier":"(0x00001f6f, 0x000016eb)","parentIdentifier":"(0xffffffff, 0xffffffff)","type":3},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"height":320,"identifier":"(0x00003cf2, 0x00002d05)","name":"Default window","parentIdentifier":"(0xffffffff, 0xffffffff)","type":1,"width":480},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"identifier":"(0x00007351, 0x00002b8e)","index":0,"name":"Default tab","parentIdentifier":"(0x00003cf2, 0x00002d05)","type":2},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":0,"identifier":"(0x0000355e, 0x00004491)","index":0,"name":"Empty","parentIdentifier":"(0x00007351, 0x00002b8e)","type":0}]</Data>
|
||||
</Entry>
|
||||
</Metadata>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x079d1f09, 0x667c7871)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x60bc58ab, 0x7a1b4d83)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x790d75b8, 0x3bb90c33)</Identifier>
|
||||
<Value>Jussi T. Lindgren</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x7a3a1558, 0xf12c63c2)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x84009d7c, 0x3c4e763b)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x8c1fc55b, 0x7b433dc2)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x9f5c4075, 0x4a0d3666)</Identifier>
|
||||
<Value>Tracker Sink example</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf36a1567, 0xd13c53da)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf6b2e3fa, 0x7bd43926)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf8034a49, 0x8b3f37cc)</Identifier>
|
||||
<Value>Inria</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</OpenViBE-Scenario>
|
||||
Masterarbeit/openvibe/extras-master/applications/platform/tracker/share/processor-example-source.xml
Executable
+729
@@ -0,0 +1,729 @@
|
||||
<OpenViBE-Scenario>
|
||||
<FormatVersion>2</FormatVersion>
|
||||
<Creator>OpenViBE Designer</Creator>
|
||||
<CreatorVersion>2.1.0</CreatorVersion>
|
||||
<Settings></Settings>
|
||||
<Inputs></Inputs>
|
||||
<Outputs></Outputs>
|
||||
<Boxes>
|
||||
<Box>
|
||||
<Identifier>(0x000020f0, 0x00006a2e)</Identifier>
|
||||
<Name>Player Controller</Name>
|
||||
<AlgorithmClassIdentifier>(0x5f426dce, 0x08456e13)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Stimulations</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
|
||||
<Name>Stimulation name</Name>
|
||||
<DefaultValue>OVTK_StimulationId_Label_00</DefaultValue>
|
||||
<Value>OVTK_StimulationId_Label_00</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0xcc14d8d6, 0xf27ecb73)</TypeIdentifier>
|
||||
<Name>Action to perform</Name>
|
||||
<DefaultValue>Pause</DefaultValue>
|
||||
<Value>Stop</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>224</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>624</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x568d148e, 0x650792b3)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x000039e9, 0x000043de)</Identifier>
|
||||
<Name>Timeout</Name>
|
||||
<AlgorithmClassIdentifier>(0x24fcd292, 0x5c8f6aa8)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x544a003e, 0x6dcba5f6)</TypeIdentifier>
|
||||
<Name>Input Stream</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Output Stimulations</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Timeout delay</Name>
|
||||
<DefaultValue>5</DefaultValue>
|
||||
<Value>5</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
|
||||
<Name>Output Stimulation</Name>
|
||||
<DefaultValue>OVTK_StimulationId_Label_00</DefaultValue>
|
||||
<Value>OVTK_StimulationId_Label_00</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>160</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>624</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x1eaee00e, 0xdb05d34e)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x000043a8, 0x000051c6)</Identifier>
|
||||
<Name>Stims</Name>
|
||||
<AlgorithmClassIdentifier>(0x65731e1d, 0x47de5276)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Stimulation stream 1</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0xa88b3667, 0x0871638c)</TypeIdentifier>
|
||||
<Name>Log level to use</Name>
|
||||
<DefaultValue>Information</DefaultValue>
|
||||
<Value>Information</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>208</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>432</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x341d3912, 0x1478de86)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0xf451ad91, 0x14c75f86)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00005438, 0x0000400f)</Identifier>
|
||||
<Name>External Processing</Name>
|
||||
<AlgorithmClassIdentifier>(0x15422959, 0x16304449)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>New input</Name>
|
||||
</Input>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>New input(1)</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Launch third party program</Name>
|
||||
<DefaultValue>true</DefaultValue>
|
||||
<Value>false</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
|
||||
<Name>Executable path</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value></Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
|
||||
<Name>Arguments</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value></Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Port</Name>
|
||||
<DefaultValue>0</DefaultValue>
|
||||
<Value>${Tracker_Port_Receive}</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Automatic connection identifier</Name>
|
||||
<DefaultValue>true</DefaultValue>
|
||||
<Value>true</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x79a9edeb, 0x245d83fc)</TypeIdentifier>
|
||||
<Name>Custom connection identifier</Name>
|
||||
<DefaultValue></DefaultValue>
|
||||
<Value></Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Incoming connection timeout</Name>
|
||||
<DefaultValue>10</DefaultValue>
|
||||
<Value>10</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Generator</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>false</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x17ee7c08, 0x94c14893)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>128</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>304</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x30a4e5c9, 0x83502953)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x6410533a, 0x11ff9fa8)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x61d11811, 0x71e65362)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>8</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf191c1c8, 0xa0123976)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00005c5f, 0x0000103e)</Identifier>
|
||||
<Name>Signal display</Name>
|
||||
<AlgorithmClassIdentifier>(0x0055be5f, 0x087bdd12)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Data</Name>
|
||||
</Input>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Stimulations</Name>
|
||||
</Input>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x6ab26b81, 0x0f8c02f3)</TypeIdentifier>
|
||||
<Name>Channel Units</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x5de046a6, 0x086340aa)</TypeIdentifier>
|
||||
<Name>Display Mode</Name>
|
||||
<DefaultValue>Scan</DefaultValue>
|
||||
<Value>Scan</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x33a30739, 0x00d5299b)</TypeIdentifier>
|
||||
<Name>Auto vertical scale</Name>
|
||||
<DefaultValue>Per channel</DefaultValue>
|
||||
<Value>Global</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Scale refresh interval (secs)</Name>
|
||||
<DefaultValue>5</DefaultValue>
|
||||
<Value>5</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Vertical Scale</Name>
|
||||
<DefaultValue>100</DefaultValue>
|
||||
<Value>2</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Vertical Offset</Name>
|
||||
<DefaultValue>0</DefaultValue>
|
||||
<Value>0</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Time Scale</Name>
|
||||
<DefaultValue>10</DefaultValue>
|
||||
<Value>10</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Bottom ruler</Name>
|
||||
<DefaultValue>true</DefaultValue>
|
||||
<Value>true</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Left ruler</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>false</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Multiview</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>false</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>128</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>464</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x92c056a7, 0x2dc71aff)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>9</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>3</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00005dd2, 0x00007417)</Identifier>
|
||||
<Name>Signals</Name>
|
||||
<AlgorithmClassIdentifier>(0x0ed76695, 0x01a69cc3)</AlgorithmClassIdentifier>
|
||||
<Inputs>
|
||||
<Input>
|
||||
<TypeIdentifier>(0x434f6587, 0x2efd2b7e)</TypeIdentifier>
|
||||
<Name>Spied EBML stream 1</Name>
|
||||
</Input>
|
||||
</Inputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x330306dd, 0x74a95f98)</TypeIdentifier>
|
||||
<Name>EBML nodes description</Name>
|
||||
<DefaultValue>${Path_Data}/plugins/tools/config-ebml-stream-spy.txt</DefaultValue>
|
||||
<Value>${Path_Data}/plugins/tools/config-ebml-stream-spy.txt</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0xa88b3667, 0x0871638c)</TypeIdentifier>
|
||||
<Name>Log level to use</Name>
|
||||
<DefaultValue>Information</DefaultValue>
|
||||
<Value>Information</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2cdb2f0b, 0x12f231ea)</TypeIdentifier>
|
||||
<Name>Expand binary blocks</Name>
|
||||
<DefaultValue>false</DefaultValue>
|
||||
<Value>true</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Number of values in expanded blocks</Name>
|
||||
<DefaultValue>4</DefaultValue>
|
||||
<Value>4</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>208</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>304</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x341d3912, 0x1478de86)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x23ce0a10, 0xc2eabb47)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>4</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xfba64161, 0x65304e21)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00005f1a, 0x000008fb)</Identifier>
|
||||
<Name>Noise generator</Name>
|
||||
<AlgorithmClassIdentifier>(0x0e3929f1, 0x15af76b9)</AlgorithmClassIdentifier>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x5ba36127, 0x195feae1)</TypeIdentifier>
|
||||
<Name>Generated signal</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Channel count</Name>
|
||||
<DefaultValue>4</DefaultValue>
|
||||
<Value>5</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Sampling frequency</Name>
|
||||
<DefaultValue>512</DefaultValue>
|
||||
<Value>256</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
|
||||
<Name>Generated epoch sample count</Name>
|
||||
<DefaultValue>32</DefaultValue>
|
||||
<Value>32</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2e85e95e, 0x8a1a8365)</TypeIdentifier>
|
||||
<Name>Noise type</Name>
|
||||
<DefaultValue>Uniform</DefaultValue>
|
||||
<Value>Gaussian</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>32</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>256</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x0b214ed8, 0x1f9ad83a)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>4</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
<Box>
|
||||
<Identifier>(0x00006300, 0x00004eef)</Identifier>
|
||||
<Name>Clock stimulator</Name>
|
||||
<AlgorithmClassIdentifier>(0x4f756d3f, 0x29ff0b96)</AlgorithmClassIdentifier>
|
||||
<Outputs>
|
||||
<Output>
|
||||
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
|
||||
<Name>Generated stimulations</Name>
|
||||
</Output>
|
||||
</Outputs>
|
||||
<Settings>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x512a166f, 0x5c3ef83f)</TypeIdentifier>
|
||||
<Name>Interstimulation interval (in sec)</Name>
|
||||
<DefaultValue>1.0</DefaultValue>
|
||||
<Value>0.5</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
<Setting>
|
||||
<TypeIdentifier>(0x2c132d6e, 0x44ab0d97)</TypeIdentifier>
|
||||
<Name>Stimulation</Name>
|
||||
<DefaultValue>OVTK_StimulationId_Label_00</DefaultValue>
|
||||
<Value>OVTK_StimulationId_Label_00</Value>
|
||||
<Modifiability>false</Modifiability>
|
||||
</Setting>
|
||||
</Settings>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
|
||||
<Value>32</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
|
||||
<Value>368</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
|
||||
<Value>(0x27b3ee3c, 0xc50527e6)</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
|
||||
<Value>1</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
|
||||
<Value>2</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Box>
|
||||
</Boxes>
|
||||
<Links>
|
||||
<Link>
|
||||
<Identifier>(0x00000ffc, 0x0000151f)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00006300, 0x00004eef)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00005c5f, 0x0000103e)</BoxIdentifier>
|
||||
<BoxInputIndex>1</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x000013b9, 0x0000017a)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x000039e9, 0x000043de)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x000020f0, 0x00006a2e)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00001de3, 0x00006da2)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00005f1a, 0x000008fb)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00005dd2, 0x00007417)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00001eb4, 0x000017bd)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00006300, 0x00004eef)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00005438, 0x0000400f)</BoxIdentifier>
|
||||
<BoxInputIndex>1</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x0000438a, 0x000026c2)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00005f1a, 0x000008fb)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00005438, 0x0000400f)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x00005738, 0x00003141)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00005f1a, 0x000008fb)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x00005c5f, 0x0000103e)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
<Link>
|
||||
<Identifier>(0x0000742a, 0x0000602a)</Identifier>
|
||||
<Source>
|
||||
<BoxIdentifier>(0x00006300, 0x00004eef)</BoxIdentifier>
|
||||
<BoxOutputIndex>0</BoxOutputIndex>
|
||||
</Source>
|
||||
<Target>
|
||||
<BoxIdentifier>(0x000043a8, 0x000051c6)</BoxIdentifier>
|
||||
<BoxInputIndex>0</BoxInputIndex>
|
||||
</Target>
|
||||
</Link>
|
||||
</Links>
|
||||
<Comments>
|
||||
<Comment>
|
||||
<Identifier>(0x00001912, 0x0000689c)</Identifier>
|
||||
<Text>Example of a processor that sends data to the Tracker,
|
||||
but does not receive anything from it: the processor
|
||||
works as a <b>Data Source</b>.
|
||||
|
||||
The generated data should appear in the Tracker
|
||||
as a new track.
|
||||
|
||||
In this case, the tracker Workspace must be
|
||||
configured to Receive but not to Send.
|
||||
</Text>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x473d9a43, 0x97fc0a97)</Identifier>
|
||||
<Value>32</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x7234b86b, 0x2b8651a5)</Identifier>
|
||||
<Value>144</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</Comment>
|
||||
</Comments>
|
||||
<Metadata>
|
||||
<Entry>
|
||||
<Identifier>(0x000000c2, 0x000011b4)</Identifier>
|
||||
<Type>(0x3bcce5d2, 0x43f2d968)</Type>
|
||||
<Data>[{"boxIdentifier":"(0x00005c5f, 0x0000103e)","childCount":0,"identifier":"(0x00001f6f, 0x000016eb)","parentIdentifier":"(0xffffffff, 0xffffffff)","type":3},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"height":320,"identifier":"(0x00003cf2, 0x00002d05)","name":"Default window","parentIdentifier":"(0xffffffff, 0xffffffff)","type":1,"width":480},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":1,"identifier":"(0x00007351, 0x00002b8e)","index":0,"name":"Default tab","parentIdentifier":"(0x00003cf2, 0x00002d05)","type":2},{"boxIdentifier":"(0xffffffff, 0xffffffff)","childCount":0,"identifier":"(0x0000355e, 0x00004491)","index":0,"name":"Empty","parentIdentifier":"(0x00007351, 0x00002b8e)","type":0}]</Data>
|
||||
</Entry>
|
||||
</Metadata>
|
||||
<Attributes>
|
||||
<Attribute>
|
||||
<Identifier>(0x079d1f09, 0x667c7871)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x60bc58ab, 0x7a1b4d83)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x790d75b8, 0x3bb90c33)</Identifier>
|
||||
<Value>Jussi T. Lindgren</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x7a3a1558, 0xf12c63c2)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x84009d7c, 0x3c4e763b)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x8c1fc55b, 0x7b433dc2)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0x9f5c4075, 0x4a0d3666)</Identifier>
|
||||
<Value>Tracker Source example</Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf36a1567, 0xd13c53da)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf6b2e3fa, 0x7bd43926)</Identifier>
|
||||
<Value></Value>
|
||||
</Attribute>
|
||||
<Attribute>
|
||||
<Identifier>(0xf8034a49, 0x8b3f37cc)</Identifier>
|
||||
<Value>Inria</Value>
|
||||
</Attribute>
|
||||
</Attributes>
|
||||
</OpenViBE-Scenario>
|
||||
+3058
File diff suppressed because it is too large
Load Diff
Executable
BIN
Binary file not shown.
+213
@@ -0,0 +1,213 @@
|
||||
#include <openvibe/ov_all.h>
|
||||
|
||||
#include <fs/Files.h>
|
||||
|
||||
#include "BoxAdapter.h"
|
||||
#include "CodecFactory.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
|
||||
bool BoxAdapter::initialize()
|
||||
{
|
||||
m_boxAlgorithm = m_kernelCtx.getPluginManager().createBoxAlgorithm(m_algorithmID, nullptr);
|
||||
if (!m_boxAlgorithm)
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Error: failed to create algorithm for id " << m_algorithmID.str() << "\n";
|
||||
return false;
|
||||
}
|
||||
m_boxAlgorithmCtx.getTrackerBoxIO()->initialize(m_boxAlgorithmCtx.getStaticBoxContext());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BoxAdapter::uninitialize()
|
||||
{
|
||||
if (m_boxAlgorithm)
|
||||
{
|
||||
m_boxAlgorithm->uninitialize(m_boxAlgorithmCtx);
|
||||
m_kernelCtx.getPluginManager().releasePluginObject(m_boxAlgorithm);
|
||||
m_boxAlgorithm = nullptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BoxAdapterBundle::~BoxAdapterBundle()
|
||||
{
|
||||
for (auto decoder : m_decoders) { delete decoder; }
|
||||
m_decoders.clear();
|
||||
for (auto encoder : m_encoders) { delete encoder; }
|
||||
m_encoders.clear();
|
||||
}
|
||||
|
||||
bool BoxAdapterBundle::initialize()
|
||||
{
|
||||
if (!BoxAdapter::initialize()) { return false; }
|
||||
|
||||
Kernel::IBox* staticBoxContext = const_cast<Kernel::IBox*>(m_boxAlgorithmCtx.getStaticBoxContext());
|
||||
|
||||
if (m_src)
|
||||
{
|
||||
for (size_t i = 0; i < m_src->getNumStreams(); ++i)
|
||||
{
|
||||
// @fixme test boxes input support here
|
||||
std::stringstream ss;
|
||||
ss << "Stream" << i; // not really used since there's no GUI for the box
|
||||
staticBoxContext->addInput(ss.str().c_str(), m_src->getStream(i)->getTypeIdentifier());
|
||||
m_encoders.push_back(CodecFactory::getEncoder(m_kernelCtx, *m_src->getStream(i)));
|
||||
if (m_dst)
|
||||
{
|
||||
m_dst->createStream(i, m_src->getStream(i)->getTypeIdentifier());
|
||||
m_decoders.push_back(CodecFactory::getDecoder(m_kernelCtx, *m_dst->getStream(i)));
|
||||
staticBoxContext->addOutput(ss.str().c_str(), m_dst->getStream(i)->getTypeIdentifier());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_boxAlgorithm->initialize(m_boxAlgorithmCtx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BoxAdapterBundle::spool(const bool /* verbose */)
|
||||
{
|
||||
Kernel::IBoxIO* boxCtx = const_cast<Kernel::IBoxIO*>(m_boxAlgorithmCtx.getDynamicBoxContext());
|
||||
TrackerBoxIO* ioCtx = static_cast<TrackerBoxIO*>(boxCtx);
|
||||
|
||||
if (!m_src)
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Error: box wrapper doesn't yet support processing without source\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_src->rewind();
|
||||
|
||||
const uint32_t nOutput = m_boxAlgorithmCtx.getStaticBoxContext()->getOutputCount();
|
||||
|
||||
while (true)
|
||||
{
|
||||
size_t index;
|
||||
StreamPtr stream = m_src->getNextStream(index);
|
||||
if (!stream) { break; }
|
||||
EncodedChunk chk;
|
||||
EChunkType outputType;
|
||||
m_encoders[index]->encode(chk, outputType);
|
||||
|
||||
ioCtx->addInputChunk(index, chk);
|
||||
|
||||
m_boxAlgorithm->process(m_boxAlgorithmCtx);
|
||||
|
||||
ioCtx->clearInputChunks();
|
||||
|
||||
if (m_dst)
|
||||
{
|
||||
for (size_t j = 0; j < nOutput; ++j)
|
||||
{
|
||||
if (ioCtx->isReadyToSend(j))
|
||||
{
|
||||
ioCtx->getOutputChunk(j, chk);
|
||||
m_decoders[j]->decode(chk);
|
||||
ioCtx->deprecateOutput(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
stream->step();
|
||||
}
|
||||
|
||||
m_src->rewind();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BoxAdapterStream::initialize()
|
||||
{
|
||||
if (!BoxAdapter::initialize()) { return false; }
|
||||
|
||||
Kernel::IBox* boxCtx = const_cast<Kernel::IBox*>(m_boxAlgorithmCtx.getStaticBoxContext());
|
||||
|
||||
CIdentifier typeID;
|
||||
boxCtx->getInputType(0, typeID);
|
||||
if (typeID != m_src->getTypeIdentifier())
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Error: Box alg first input stream is wrong type\n";
|
||||
return false;
|
||||
}
|
||||
boxCtx->getOutputType(0, typeID);
|
||||
if (typeID != m_dst->getTypeIdentifier())
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Error: Box alg first output stream is wrong type\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_boxAlgorithm->initialize(m_boxAlgorithmCtx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BoxAdapterStream::spool(const bool verbose)
|
||||
{
|
||||
Kernel::IBoxIO* boxCtx = const_cast<Kernel::IBoxIO*>(m_boxAlgorithmCtx.getDynamicBoxContext());
|
||||
TrackerBoxIO* ioCtx = static_cast<TrackerBoxIO*>(boxCtx);
|
||||
|
||||
if (!m_src)
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Error: box wrapper doesn't yet support processing without source\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_src->reset();
|
||||
|
||||
EncoderBase* encoder = CodecFactory::getEncoder(m_kernelCtx, *m_src);
|
||||
DecoderBase* decoder = CodecFactory::getDecoder(m_kernelCtx, *m_dst);
|
||||
|
||||
if (verbose) { log() << Kernel::LogLevel_Info; }
|
||||
|
||||
bool finished = false;
|
||||
uint64_t cnt = 0;
|
||||
while (!finished)
|
||||
{
|
||||
if (m_src)
|
||||
{
|
||||
EncodedChunk chk;
|
||||
EChunkType outputType;
|
||||
if (encoder->encode(chk, outputType))
|
||||
{
|
||||
// @fixme here we assume the box takes data in at the first slot
|
||||
ioCtx->clearInputChunks();
|
||||
ioCtx->addInputChunk(0, chk);
|
||||
|
||||
m_src->step();
|
||||
}
|
||||
else { finished = true; }
|
||||
}
|
||||
|
||||
m_boxAlgorithm->process(m_boxAlgorithmCtx);
|
||||
|
||||
if (m_dst)
|
||||
{
|
||||
if (ioCtx->isReadyToSend(0))
|
||||
{
|
||||
EncodedChunk chk;
|
||||
ioCtx->getOutputChunk(0, chk);
|
||||
decoder->decode(chk);
|
||||
ioCtx->deprecateOutput(0);
|
||||
}
|
||||
}
|
||||
if (verbose && cnt++ % 100 == 0) { log() << "."; }
|
||||
}
|
||||
if (verbose) { log() << "\n"; }
|
||||
|
||||
m_src->reset();
|
||||
|
||||
ioCtx->clearInputChunks();
|
||||
|
||||
delete encoder;
|
||||
delete decoder;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
#include "BoxPlugins.h"
|
||||
#include "ovkCBoxProto.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
#define OVP_ClassId_BoxAlgorithm_TemporalFilter CIdentifier(0xB4F9D042, 0x9D79F2E5)
|
||||
#define OVP_ClassId_BoxAlgorithm_TemporalFilterDesc CIdentifier(0x7BF6BA62, 0xAF829A37)
|
||||
#define OVP_ClassId_BoxAlgorithm_SignalResampling CIdentifier(0x0E923A5E, 0xDA474058)
|
||||
#define OVP_ClassId_BoxAlgorithm_SignalResamplingDesc CIdentifier(0xA675A433, 0xC6690920)
|
||||
#define OVP_ClassId_BoxAlgorithm_SimpleDSP CIdentifier(0x00E26FA1, 0x1DBAB1B2)
|
||||
#define OVP_ClassId_BoxAlgorithm_SimpleDSPDesc CIdentifier(0x00C44BFE, 0x76C9269E)
|
||||
#define OVP_ClassId_BoxAlgorithm_Crop CIdentifier(0x7F1A3002, 0x358117BA)
|
||||
#define OVP_ClassId_BoxAlgorithm_CropDesc CIdentifier(0x64D619D7, 0x26CC42C9)
|
||||
#define OVP_ClassId_BoxAlgorithm_SpatialFilter CIdentifier(0xDD332C6C, 0x195B4FD4)
|
||||
#define OVP_ClassId_BoxAlgorithm_SpatialFilterDesc CIdentifier(0x72A01C92, 0xF8C1FA24)
|
||||
#define OVP_ClassId_BoxAlgorithm_TimeBasedEpoching CIdentifier(0x00777FA0, 0x5DC3F560)
|
||||
#define OVP_ClassId_BoxAlgorithm_TimeBasedEpochingDesc CIdentifier(0x00ABDABE, 0x41381683)
|
||||
#define OVP_ClassId_BoxAlgorithm_StimulationFilter CIdentifier(0x02F96101, 0x5E647CB8)
|
||||
#define OVP_ClassId_BoxAlgorithm_StimulationFilterDesc CIdentifier(0x4D2A23FC, 0x28191E18)
|
||||
#define OVP_ClassId_FastICA CIdentifier(0x00649B6E, 0x6C88CD17)
|
||||
#define OVP_ClassId_FastICADesc CIdentifier(0x00E9436C, 0x41C904CA)
|
||||
#define OVP_ClassId_BoxAlgorithm_CommonAverageReference CIdentifier(0x009C0CE3, 0x6BDF71C3)
|
||||
#define OVP_ClassId_BoxAlgorithm_CommonAverageReferenceDesc CIdentifier(0x0033EAF8, 0x09C65E4E)
|
||||
#define OVP_ClassId_BoxAlgorithm_ChannelSelector CIdentifier(0x361722E8, 0x311574E8)
|
||||
#define OVP_ClassId_BoxAlgorithm_ChannelSelectorDesc CIdentifier(0x67633C1C, 0x0D610CD8)
|
||||
#define OVP_ClassId_BoxAlgorithm_ChannelRename CIdentifier(0x1FE50479, 0x39040F40)
|
||||
#define OVP_ClassId_BoxAlgorithm_ChannelRenameDesc CIdentifier(0x20EA1F00, 0x7AED5645)
|
||||
#define OVP_ClassId_BoxAlgorithm_ReferenceChannel CIdentifier(0x444721AD, 0x78342CF5)
|
||||
#define OVP_ClassId_BoxAlgorithm_ReferenceChannelDesc CIdentifier(0x42856103, 0x45B125AD)
|
||||
#define OVP_ClassId_BoxAlgorithm_FrequencyBandSelector CIdentifier(0x140C19C6, 0x4E6E187B)
|
||||
#define OVP_ClassId_BoxAlgorithm_FrequencyBandSelectorDesc CIdentifier(0x13462C56, 0x794E3C07)
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
BoxPlugins::BoxPlugins(const Kernel::IKernelContext& ctx) : Contexted(ctx)
|
||||
{
|
||||
// Register some boxes that can be used as filters
|
||||
create(OV_TypeId_Signal, OVP_ClassId_BoxAlgorithm_TemporalFilter, OVP_ClassId_BoxAlgorithm_TemporalFilterDesc);
|
||||
create(OV_TypeId_Signal, OVP_ClassId_BoxAlgorithm_SimpleDSP, OVP_ClassId_BoxAlgorithm_SimpleDSPDesc);
|
||||
create(OV_TypeId_Signal, OVP_ClassId_BoxAlgorithm_TimeBasedEpoching, OVP_ClassId_BoxAlgorithm_TimeBasedEpochingDesc);
|
||||
create(OV_TypeId_Signal, OVP_ClassId_BoxAlgorithm_Crop, OVP_ClassId_BoxAlgorithm_CropDesc);
|
||||
create(OV_TypeId_Signal, OVP_ClassId_BoxAlgorithm_SpatialFilter, OVP_ClassId_BoxAlgorithm_SpatialFilterDesc);
|
||||
create(OV_TypeId_Signal, OVP_ClassId_BoxAlgorithm_SignalResampling, OVP_ClassId_BoxAlgorithm_SignalResamplingDesc);
|
||||
create(OV_TypeId_Signal, OVP_ClassId_FastICA, OVP_ClassId_FastICADesc);
|
||||
create(OV_TypeId_Signal, OVP_ClassId_BoxAlgorithm_CommonAverageReference, OVP_ClassId_BoxAlgorithm_CommonAverageReferenceDesc);
|
||||
|
||||
create(OV_TypeId_Signal, OVP_ClassId_BoxAlgorithm_ChannelSelector, OVP_ClassId_BoxAlgorithm_ChannelSelectorDesc);
|
||||
create(OV_TypeId_Signal, OVP_ClassId_BoxAlgorithm_ChannelRename, OVP_ClassId_BoxAlgorithm_ChannelRenameDesc);
|
||||
create(OV_TypeId_Signal, OVP_ClassId_BoxAlgorithm_ReferenceChannel, OVP_ClassId_BoxAlgorithm_ReferenceChannelDesc);
|
||||
|
||||
create(OV_TypeId_Spectrum, OVP_ClassId_BoxAlgorithm_FrequencyBandSelector, OVP_ClassId_BoxAlgorithm_FrequencyBandSelectorDesc);
|
||||
|
||||
create(OV_TypeId_Stimulations, OVP_ClassId_BoxAlgorithm_StimulationFilter, OVP_ClassId_BoxAlgorithm_StimulationFilterDesc);
|
||||
|
||||
std::sort(m_boxPlugins.begin(), m_boxPlugins.end(), [](BoxAdapterStream* a, BoxAdapterStream* b)
|
||||
{
|
||||
return (a->getBox().getName()) < (b->getBox().getName());
|
||||
});
|
||||
}
|
||||
|
||||
bool BoxPlugins::create(const CIdentifier& streamType, const CIdentifier& alg, const CIdentifier& desc)
|
||||
{
|
||||
BoxAdapterStream* ptr = new BoxAdapterStream(m_kernelCtx, alg);
|
||||
|
||||
// Initialize the context by polling the descriptor
|
||||
const Plugins::IPluginObjectDesc* pod = getKernelContext().getPluginManager().getPluginObjectDesc(desc);
|
||||
if (!pod)
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Unable to load box algorithm " << alg.str() << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
const Plugins::IBoxAlgorithmDesc* pBoxAlgorithmDescriptor = dynamic_cast<const Plugins::IBoxAlgorithmDesc*>(pod);
|
||||
Kernel::CBoxProto boxProto(m_kernelCtx, ptr->getBox());
|
||||
pBoxAlgorithmDescriptor->getBoxPrototype(boxProto);
|
||||
|
||||
// We need to force this so we don't launch the plugin on streams of other types, and not all boxes declare
|
||||
// their capabilities. Here we assume that if the box is created for this stream type, it can support it.
|
||||
boxProto.addInputSupport(streamType);
|
||||
|
||||
CIdentifier typeID;
|
||||
ptr->getBox().getInputType(0, typeID);
|
||||
if (typeID != streamType) { ptr->getBox().setInputType(0, streamType); }
|
||||
ptr->getBox().getOutputType(0, typeID);
|
||||
if (typeID != streamType) { ptr->getBox().setOutputType(0, streamType); }
|
||||
|
||||
const CString boxName = m_kernelCtx.getTypeManager().getTypeName(streamType) + CString(" : ") + pBoxAlgorithmDescriptor->getName();
|
||||
ptr->getBox().setName(boxName);
|
||||
|
||||
m_boxPlugins.push_back(ptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,454 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
// @todo catch way more errors, currently most return values are ignored by callers
|
||||
// @todo Change error handling to use the macros introduced in certivibe
|
||||
// @todo clean the code to be more conforming to coding rules
|
||||
// @todo Implement vertical slider / scale
|
||||
// @todo Fix time units
|
||||
// @todo Add track renderer only when the track appears
|
||||
//
|
||||
// @todo add dataset manager (workspace == datasets)
|
||||
// @todo enable a view where only 'current track' is shown and the rest are minimized (unloaded?)
|
||||
// @todo add option to collapse tracks to take less visual space
|
||||
// @todo add record button
|
||||
// @todo implement undo
|
||||
// @todo fix issues with empty tracks/streams and if the processing fails (e.g. incompatible processor)
|
||||
// @todo fix creation of noncontinuous streams in the catenate mode e.g. by padding all source streams to equal length?
|
||||
// @todo write some tutorial processors, e.g. erp analysis, file export ... ?
|
||||
// @todo allow running several scenarios sequentially
|
||||
// @todo selection could be saved in the .ovw file simply as track/stream tokens (small perf hit)?
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
#include "CTracker.h"
|
||||
#include "StreamBundleImportExport.h"
|
||||
|
||||
#include "Workspace.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
CTracker::CTracker(const Kernel::IKernelContext& ctx)
|
||||
: Contexted(ctx), m_workspace(ctx), m_boxPlugins(ctx), m_trackerPlugins(ctx)
|
||||
{
|
||||
// Default config load deferred until initialize() which can be called after booting the GUI
|
||||
// --> so we can get the possible log messages to the GUI
|
||||
}
|
||||
|
||||
CTracker::~CTracker()
|
||||
{
|
||||
const CString configFile = m_kernelCtx.getConfigurationManager().expand("${Path_UserData}/openvibe-tracker.conf");
|
||||
saveConfig(configFile);
|
||||
m_executor.uninitialize();
|
||||
}
|
||||
|
||||
bool CTracker::initialize()
|
||||
{
|
||||
const CString configFile = m_kernelCtx.getConfigurationManager().expand("${Path_UserData}/openvibe-tracker.conf");
|
||||
// n.b. Initing the executor is in loadConfig so its not done twice
|
||||
m_workspace.setParallelExecutor(&m_executor);
|
||||
return loadConfig(configFile);
|
||||
}
|
||||
|
||||
bool CTracker::play(const bool playFast)
|
||||
{
|
||||
switch (m_state)
|
||||
{
|
||||
case EStates::Stopped:
|
||||
if (!m_workspace.play(playFast))
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Error: play failed\n";
|
||||
return false;
|
||||
}
|
||||
m_state = EStates::Playing;
|
||||
break;
|
||||
case EStates::Playing:
|
||||
m_state = EStates::Paused;
|
||||
break;
|
||||
case EStates::Paused:
|
||||
m_state = EStates::Playing;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CTracker::stop()
|
||||
{
|
||||
// Request workspace to stop, do not change state until it has stopped
|
||||
return m_workspace.stop();
|
||||
}
|
||||
|
||||
CTracker::EStates CTracker::step()
|
||||
{
|
||||
if (m_state == EStates::Playing) { if (!m_workspace.step()) { m_state = EStates::Stopping; } }
|
||||
else if (m_state == EStates::Stopping) { if (m_executor.isIdle()) { m_state = EStates::Stopped; } }
|
||||
else { System::Time::sleep(1); }
|
||||
|
||||
return m_state;
|
||||
}
|
||||
|
||||
struct Workpackage
|
||||
{
|
||||
StreamBundle* source = nullptr;
|
||||
std::mutex oMutex;
|
||||
size_t trackIndex;
|
||||
std::deque<size_t> streamsToProcess;
|
||||
};
|
||||
|
||||
|
||||
bool CTracker::applyBoxPlugin(const size_t index)
|
||||
{
|
||||
auto& plugins = getBoxPlugins().getBoxPlugins();
|
||||
if (index >= plugins.size())
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Plugin index exceeds plugin array size\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getWorkspace().getMemorySaveMode() && getWorkspace().getWorkingPath().length() == 0)
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Memory save mode requires a workspace path\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
const CTime startTime = CTime(System::Time::zgetTime());
|
||||
|
||||
BoxAdapterStream* box = plugins[index];
|
||||
|
||||
const CString name = box->getBox().getName();
|
||||
log() << Kernel::LogLevel_Info << "Applying method " << name << " (" << (m_workspace.getInplaceMode() ? "Inplace" : "Normal") << " mode)\n";
|
||||
|
||||
for (size_t trackIndex = 0; trackIndex < getWorkspace().getNumTracks(); ++trackIndex)
|
||||
{
|
||||
Workpackage* wptr = new Workpackage;
|
||||
Workspace& ws = getWorkspace();
|
||||
|
||||
StreamBundle* sourceTrack = ws.getTrack(trackIndex);
|
||||
|
||||
// Count
|
||||
const size_t numStreams = ws.getTrack(trackIndex)->getNumStreams();
|
||||
for (size_t j = 0; j < numStreams; ++j)
|
||||
{
|
||||
if (sourceTrack->getStream(j)->getSelected() && box->getBox().hasInputSupport(sourceTrack->getStream(j)->getTypeIdentifier()))
|
||||
{
|
||||
wptr->streamsToProcess.push_back(j);
|
||||
}
|
||||
}
|
||||
const size_t nToProcess = wptr->streamsToProcess.size();
|
||||
if (nToProcess == 0) { continue; }
|
||||
|
||||
wptr->trackIndex = trackIndex;
|
||||
wptr->source = (m_workspace.getMemorySaveMode() ? nullptr : sourceTrack);
|
||||
|
||||
for (size_t j = 0; j < nToProcess; ++j)
|
||||
{
|
||||
const bool inplaceMode = m_workspace.getInplaceMode();
|
||||
const bool multithread = (m_executor.getNumThreads() > 1);
|
||||
const bool memorySave = m_workspace.getMemorySaveMode();
|
||||
|
||||
const Kernel::IKernelContext& ctx = getKernelContext();
|
||||
|
||||
auto job = [&ws, wptr, &ctx, box, multithread, inplaceMode, memorySave](uint32_t /*threadNumber*/)
|
||||
{
|
||||
size_t sourceStreamIndex;
|
||||
{
|
||||
std::unique_lock<std::mutex>(wptr->oMutex);
|
||||
if (memorySave && !wptr->source)
|
||||
{
|
||||
wptr->source = readStreamBundleFromFile(ctx,
|
||||
ws.getTrack(wptr->trackIndex)->getSource().c_str(), false);
|
||||
}
|
||||
sourceStreamIndex = wptr->streamsToProcess.front();
|
||||
wptr->streamsToProcess.pop_front();
|
||||
}
|
||||
|
||||
StreamBundle* sourceTrack = wptr->source;
|
||||
const StreamPtr sourceStream = sourceTrack->getStream(sourceStreamIndex);
|
||||
|
||||
// process
|
||||
|
||||
if (!multithread)
|
||||
{
|
||||
ctx.getLogManager() << Kernel::LogLevel_Info << "Processing track " << (wptr->trackIndex + 1) << " stream " << (sourceStreamIndex + 1) << "\n";
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex>(wptr->oMutex);
|
||||
|
||||
const size_t targetStreamIndex = sourceTrack->getNumStreams();
|
||||
sourceTrack->createStream(targetStreamIndex, sourceStream->getTypeIdentifier());
|
||||
StreamPtr targetStream = sourceTrack->getStream(targetStreamIndex);
|
||||
|
||||
// We need a copy of the box since otherwise different threads init the same box differently
|
||||
BoxAdapterStream* boxCopy = new BoxAdapterStream(ctx, box->getAlgorithmId());
|
||||
|
||||
boxCopy->getBox().initializeFromExistingBox(box->getBox());
|
||||
|
||||
boxCopy->setSource(sourceStream);
|
||||
boxCopy->setTarget(targetStream);
|
||||
|
||||
if (!boxCopy->initialize())
|
||||
{
|
||||
std::unique_lock<std::mutex>(wptr->oMutex);
|
||||
|
||||
sourceTrack->deleteStream(targetStreamIndex);
|
||||
delete boxCopy;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
boxCopy->spool(!multithread);
|
||||
boxCopy->uninitialize();
|
||||
delete boxCopy;
|
||||
|
||||
if (inplaceMode)
|
||||
{
|
||||
std::unique_lock<std::mutex>(wptr->oMutex);
|
||||
|
||||
sourceTrack->swapStreams(sourceStreamIndex, targetStreamIndex);
|
||||
sourceTrack->deleteStream(targetStreamIndex);
|
||||
}
|
||||
else { targetStream->setSelected(false); }
|
||||
|
||||
// Spool resources to disk and free memory
|
||||
{
|
||||
std::unique_lock<std::mutex>(wptr->oMutex);
|
||||
|
||||
if (memorySave)
|
||||
{
|
||||
// Free resources if this thread is the last
|
||||
if (wptr->streamsToProcess.empty())
|
||||
{
|
||||
if (wptr->source->getDirtyBit())
|
||||
{
|
||||
// @fixme not a good solution with the filenaming; rethink the whole thing
|
||||
|
||||
std::stringstream ss;
|
||||
ss << ws.getWorkingPath() << "/workspace-track-" << (wptr->trackIndex + 1) << ".ov";
|
||||
|
||||
// In this mode, sourceTrack is not from the track array. We spool it to disk, then
|
||||
// set the filename of the track in the array, and reload it back.
|
||||
saveStreamBundleToFile(ctx, wptr->source, ss.str().c_str());
|
||||
ws.getTrack(wptr->trackIndex)->setSource(ss.str());
|
||||
ws.reloadTrack(wptr->trackIndex);
|
||||
}
|
||||
delete wptr->source;
|
||||
}
|
||||
}
|
||||
if (wptr->streamsToProcess.empty()) { delete wptr; }
|
||||
}
|
||||
};
|
||||
|
||||
m_executor.pushJob(job);
|
||||
|
||||
if (!multithread) { m_executor.waitForAll(); }
|
||||
}
|
||||
}
|
||||
|
||||
m_executor.waitForAll();
|
||||
|
||||
// pBoxAlgorithmDescriptor->release();
|
||||
|
||||
const CTime elapsed = CTime(System::Time::zgetTime()) - startTime;
|
||||
log() << Kernel::LogLevel_Info << "Applying plugin took " << elapsed.toSeconds() << " sec.\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CTracker::applyTrackerPlugin(const size_t index)
|
||||
{
|
||||
auto& plugins = getTrackerPlugins().getTrackerPlugins();
|
||||
if (index >= plugins.size())
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Plugin index exceeds plugin array size\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getWorkspace().getMemorySaveMode() && getWorkspace().getWorkingPath().length() == 0)
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Memory save mode requires a workspace path\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
ITrackerPlugin* plugin = plugins[index];
|
||||
|
||||
const CString name(plugin->getName().c_str());
|
||||
log() << Kernel::LogLevel_Info << "Applying method " << name << " (" << (m_workspace.getInplaceMode() ? "Inplace" : "Normal") << " mode)\n";
|
||||
|
||||
bool retVal = true;
|
||||
|
||||
if (plugin->hasCapability(ITrackerPlugin::ECapabilities::Workspace))
|
||||
{
|
||||
retVal = plugin->process(getWorkspace(), m_executor);
|
||||
if (!retVal) { log() << Kernel::LogLevel_Error << "Error processing workspace with the plugin\n"; }
|
||||
}
|
||||
else if (plugin->hasCapability(ITrackerPlugin::ECapabilities::Tracks))
|
||||
{
|
||||
// @note since the different jobs handle different tracks, we don't do locking here.
|
||||
for (size_t trackIndex = 0; trackIndex < getWorkspace().getNumTracks(); ++trackIndex)
|
||||
{
|
||||
ITrackerPlugin* pluginCopy = getTrackerPlugins().getPluginCopy(index);
|
||||
|
||||
Workspace& ws = getWorkspace();
|
||||
const Kernel::IKernelContext& ctx = getKernelContext();
|
||||
|
||||
auto job = [pluginCopy,&ws,&ctx,trackIndex](uint32_t /*threadNumber*/)
|
||||
{
|
||||
StreamBundle* sourceTrack;
|
||||
if (ws.getMemorySaveMode())
|
||||
{
|
||||
ctx.getLogManager() << Kernel::LogLevel_Info << "Loading " << ws.getTrack(trackIndex)->getSource().c_str()
|
||||
<< " from file\n";
|
||||
|
||||
sourceTrack = readStreamBundleFromFile(ctx,
|
||||
ws.getTrack(trackIndex)->getSource().c_str(), false);
|
||||
}
|
||||
else { sourceTrack = ws.getTrack(trackIndex); }
|
||||
|
||||
|
||||
if (!pluginCopy->process(*sourceTrack))
|
||||
{
|
||||
// log() << Kernel::LogLevel_Error << "Error processing track << " << (i + 1) << " with the plugin\n";
|
||||
// retVal = false;
|
||||
}
|
||||
delete pluginCopy;
|
||||
|
||||
if (ws.getMemorySaveMode())
|
||||
{
|
||||
if (sourceTrack->getDirtyBit())
|
||||
{
|
||||
// @fixme not a good solution with the filenaming; rethink the whole thing
|
||||
|
||||
std::stringstream ss;
|
||||
ss << ws.getWorkingPath() << "/workspace-track-" << (trackIndex + 1) << ".ov";
|
||||
|
||||
// In this mode, sourceTrack is not from the track array. We spool it to disk, then
|
||||
// set the filename of the track in the array, and reload it back.
|
||||
saveStreamBundleToFile(ctx, sourceTrack, ss.str().c_str());
|
||||
ws.getTrack(trackIndex)->setSource(ss.str());
|
||||
ws.reloadTrack(trackIndex);
|
||||
}
|
||||
|
||||
delete sourceTrack;
|
||||
}
|
||||
};
|
||||
|
||||
m_executor.pushJob(job);
|
||||
|
||||
// @todo copy sourcetrack, process the copy, if the processing is
|
||||
// successful then replace the original (or not) depending on inplacemode setting
|
||||
|
||||
if (m_executor.getNumThreads() == 1) { m_executor.waitForAll(); }
|
||||
}
|
||||
// @todo in principle there's no need to freeze the GUI meanwhile, but allowing it to run in the bg would require locking
|
||||
m_executor.waitForAll();
|
||||
}
|
||||
else { log() << Kernel::LogLevel_Error << "Plugin does not have any known capabilities and cannot be run.\n"; }
|
||||
|
||||
// pBoxAlgorithmDescriptor->release();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
bool CTracker::setNumThreads(uint32_t numThreads)
|
||||
{
|
||||
if (numThreads != m_executor.getNumThreads())
|
||||
{
|
||||
if (numThreads < 1)
|
||||
{
|
||||
log() << Kernel::LogLevel_Warning << "Minimum number of threads is 1, setting that.\n";
|
||||
numThreads = 1;
|
||||
}
|
||||
else if (numThreads > 1)
|
||||
{
|
||||
log() << Kernel::LogLevel_Info << "Using " << numThreads <<
|
||||
" threads. Concurrency control has not been carefully tested. If you notice issues, switch to 1 thread.\n";
|
||||
}
|
||||
m_executor.uninitialize();
|
||||
return m_executor.initialize(numThreads);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CTracker::loadConfig(const CString& filename)
|
||||
{
|
||||
if (!m_kernelCtx.getConfigurationManager().addConfigurationFromFile(filename))
|
||||
{
|
||||
m_executor.initialize(1);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t numThreads = uint32_t(m_kernelCtx.getConfigurationManager().expandAsUInteger("${Tracker_NumThreads}", 1));
|
||||
m_executor.initialize(numThreads);
|
||||
// m_Executor.launchTest();
|
||||
|
||||
const CString workspaceFile = m_kernelCtx.getConfigurationManager().expand("${Tracker_Last_Workspace}");
|
||||
|
||||
//if (m_kernelCtx.getConfigurationManager().lookUpConfigurationTokenIdentifier("Tracker_Last_Workspace"))
|
||||
// != CIdentifier::undefined())
|
||||
//{
|
||||
// m_Workspace.load(m_kernelCtx.getConfigurationManager().expand("${Tracker_Last_Workspace}");
|
||||
// Tracker_Last_Workspace
|
||||
// )
|
||||
if (workspaceFile.length() != 0) { return m_workspace.load(workspaceFile.toASCIIString()); }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// @note : does not save the workspace itself
|
||||
bool CTracker::saveConfig(const CString& filename) const
|
||||
{
|
||||
FILE* file = fopen(filename, "wt");
|
||||
if (file)
|
||||
{
|
||||
fprintf(file, "# Configuration file for OpenViBE Tracker, autosaved on Tracker exit\n");
|
||||
fprintf(file, "#\n");
|
||||
fprintf(file, "\n");
|
||||
fprintf(file, "# Last settings used in the Tracker\n");
|
||||
fprintf(file, "Tracker_Last_Workspace = %s\n", m_workspace.getFilename().toASCIIString());
|
||||
fprintf(file, "Tracker_NumThreads = %d\n", m_executor.getNumThreads());
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
else { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void testCode()
|
||||
{
|
||||
Workspace wp(*kernelWrapper.m_kernelCtx);
|
||||
|
||||
// TestClass tmp(*kernelWrapper.m_kernelCtx);
|
||||
|
||||
/*
|
||||
|
||||
const CString eegFile = Directories::getDataDir() + CString("/scenarios/signals/bci-motor-imagery.ov");
|
||||
// const CString eegFile = CString("E:/jl/noise-test.ov");
|
||||
const CString scenarioFile = Directories::getDataDir() + CString("/applications/tracker/tracker-debug-display.xml");
|
||||
|
||||
if(!wp.setTrack(eegFile.toASCIIString())) { return 2; }
|
||||
if(!wp.setprocessor(scenarioFile.toASCIIString())) { return 3; }
|
||||
|
||||
// Push some chunks to selection
|
||||
Selection& selection = wp.m_track.m_Selection;
|
||||
selection.addRange(Range(3,5));
|
||||
selection.addRange(Range(9,11));
|
||||
|
||||
if(!wp.play()) { return 4; }
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+55
@@ -0,0 +1,55 @@
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "TypeChannelLocalization.h"
|
||||
|
||||
#include "Decoder.h"
|
||||
#include "Encoder.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
template <>
|
||||
bool DecoderAdapter<TypeChannelLocalization, Toolkit::TChannelLocalisationDecoder<BoxAlgorithmProxy>>::getHeaderImpl(
|
||||
TypeChannelLocalization::Header& h)
|
||||
{
|
||||
h.m_Dynamic = m_impl.getOutputDynamic();
|
||||
|
||||
CMatrix* decoded = m_impl.getOutputMatrix();
|
||||
h.m_Header.copy(*decoded);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool DecoderAdapter<TypeChannelLocalization, Toolkit::TChannelLocalisationDecoder<BoxAlgorithmProxy>>::getBufferImpl(
|
||||
TypeChannelLocalization::Buffer& b)
|
||||
{
|
||||
CMatrix* decoded = m_impl.getOutputMatrix();
|
||||
b.m_buffer.copy(*decoded);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool EncoderAdapter<TypeChannelLocalization, Toolkit::TChannelLocalisationEncoder<BoxAlgorithmProxy>>::encodeHeaderImpl(
|
||||
const TypeChannelLocalization::Header& hdr)
|
||||
{
|
||||
m_impl.getInputDynamic() = hdr.m_Dynamic;
|
||||
|
||||
CMatrix* header = m_impl.getInputMatrix();
|
||||
header->copy(hdr.m_Header);
|
||||
|
||||
return m_impl.encodeHeader();
|
||||
}
|
||||
|
||||
template <>
|
||||
bool EncoderAdapter<TypeChannelLocalization, Toolkit::TChannelLocalisationEncoder<BoxAlgorithmProxy>>::encodeBufferImpl(
|
||||
const TypeChannelLocalization::Buffer& buf)
|
||||
{
|
||||
CMatrix* buffer = m_impl.getInputMatrix();
|
||||
buffer->copy(buf.m_buffer);
|
||||
|
||||
return m_impl.encodeBuffer();
|
||||
}
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "TypeChannelUnits.h"
|
||||
|
||||
#include "Decoder.h"
|
||||
#include "Encoder.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
template <>
|
||||
bool DecoderAdapter<TypeChannelUnits, Toolkit::TChannelUnitsDecoder<BoxAlgorithmProxy>>::getHeaderImpl(TypeChannelUnits::Header& h)
|
||||
{
|
||||
h.m_Dynamic = m_impl.getOutputDynamic();
|
||||
|
||||
CMatrix* decoded = m_impl.getOutputMatrix();
|
||||
h.m_Header.copy(*decoded);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool DecoderAdapter<TypeChannelUnits, Toolkit::TChannelUnitsDecoder<BoxAlgorithmProxy>>::getBufferImpl(TypeChannelUnits::Buffer& b)
|
||||
{
|
||||
CMatrix* decoded = m_impl.getOutputMatrix();
|
||||
b.m_buffer.copy(*decoded);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool EncoderAdapter<TypeChannelUnits, Toolkit::TChannelUnitsEncoder<BoxAlgorithmProxy>>::encodeHeaderImpl(const TypeChannelUnits::Header& hdr)
|
||||
{
|
||||
m_impl.getInputDynamic() = hdr.m_Dynamic;
|
||||
|
||||
CMatrix* header = m_impl.getInputMatrix();
|
||||
header->copy(hdr.m_Header);
|
||||
|
||||
return m_impl.encodeHeader();
|
||||
}
|
||||
|
||||
template <>
|
||||
bool EncoderAdapter<TypeChannelUnits, Toolkit::TChannelUnitsEncoder<BoxAlgorithmProxy>>::encodeBufferImpl(const TypeChannelUnits::Buffer& buf)
|
||||
{
|
||||
CMatrix* buffer = m_impl.getInputMatrix();
|
||||
buffer->copy(buf.m_buffer);
|
||||
|
||||
return m_impl.encodeBuffer();
|
||||
}
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "TypeExperimentInfo.h"
|
||||
|
||||
#include "Decoder.h"
|
||||
#include "Encoder.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
template <>
|
||||
bool DecoderAdapter<TypeExperimentInfo, Toolkit::TExperimentInfoDecoder<BoxAlgorithmProxy>>::getHeaderImpl(
|
||||
TypeExperimentInfo::Header& h)
|
||||
{
|
||||
h.m_ExperimentID = m_impl.getOutputExperimentID();
|
||||
h.m_ExperimentDate = (*m_impl.getOutputExperimentDate()).toASCIIString();
|
||||
|
||||
h.m_SubjectID = m_impl.getOutputSubjectID();
|
||||
h.m_SubjectName = (*m_impl.getOutputSubjectName()).toASCIIString();
|
||||
h.m_SubjectAge = m_impl.getOutputSubjectAge();
|
||||
h.m_SubjectGender = m_impl.getOutputSubjectGender();
|
||||
|
||||
h.m_LaboratoryID = m_impl.getOutputLaboratoryID();
|
||||
h.m_LaboratoryName = (*m_impl.getOutputLaboratoryName()).toASCIIString();
|
||||
h.m_TechnicianID = m_impl.getOutputTechnicianID();
|
||||
h.m_TechnicianName = (*m_impl.getOutputTechnicianName()).toASCIIString();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool DecoderAdapter<TypeExperimentInfo, Toolkit::TExperimentInfoDecoder<BoxAlgorithmProxy>>::getBufferImpl(TypeExperimentInfo::Buffer& /*b*/)
|
||||
{
|
||||
// Should be no buffer in the experiment stream
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool EncoderAdapter<TypeExperimentInfo, Toolkit::TExperimentInfoEncoder<BoxAlgorithmProxy>>::encodeHeaderImpl(
|
||||
const TypeExperimentInfo::Header& hdr)
|
||||
{
|
||||
// @fixme the new() calls may imply memory leaks, a bit odd the codec takes pointers
|
||||
m_impl.getInputExperimentID() = hdr.m_ExperimentID;
|
||||
m_impl.getInputExperimentDate() = new CString(hdr.m_ExperimentDate.c_str());
|
||||
|
||||
m_impl.getInputSubjectID() = hdr.m_SubjectID;
|
||||
m_impl.getInputSubjectName() = new CString(hdr.m_SubjectName.c_str());
|
||||
m_impl.getInputSubjectAge() = hdr.m_SubjectAge;
|
||||
m_impl.getInputSubjectGender() = hdr.m_SubjectGender;
|
||||
|
||||
m_impl.getInputLaboratoryID() = hdr.m_LaboratoryID;
|
||||
m_impl.getInputLaboratoryName() = new CString(hdr.m_LaboratoryName.c_str());
|
||||
m_impl.getInputTechnicianID() = hdr.m_TechnicianID;
|
||||
m_impl.getInputTechnicianName() = new CString(hdr.m_TechnicianName.c_str());
|
||||
|
||||
return m_impl.encodeHeader();
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
bool EncoderAdapter<TypeExperimentInfo, Toolkit::TExperimentInfoEncoder<BoxAlgorithmProxy>>::encodeBufferImpl(
|
||||
const TypeExperimentInfo::Buffer& /*buf*/)
|
||||
{
|
||||
// Should be no buffer in the experiment stream
|
||||
return m_impl.encodeBuffer();
|
||||
}
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
// @todo this is identical to CodecFeatureMatrix. Refactor?
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "TypeFeatureVector.h"
|
||||
|
||||
#include "Decoder.h"
|
||||
#include "Encoder.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
template <>
|
||||
bool DecoderAdapter<TypeFeatureVector, Toolkit::TFeatureVectorDecoder<BoxAlgorithmProxy>>::getHeaderImpl(TypeFeatureVector::Header& h)
|
||||
{
|
||||
CMatrix* decoded = m_impl.getOutputMatrix();
|
||||
h.m_Header.copy(*decoded);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool DecoderAdapter<TypeFeatureVector, Toolkit::TFeatureVectorDecoder<BoxAlgorithmProxy>>::getBufferImpl(TypeFeatureVector::Buffer& b)
|
||||
{
|
||||
const CMatrix* decoded = m_impl.getOutputMatrix();
|
||||
b.m_buffer.copy(*decoded);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool EncoderAdapter<TypeFeatureVector, Toolkit::TFeatureVectorEncoder<BoxAlgorithmProxy>>::encodeHeaderImpl(
|
||||
const TypeFeatureVector::Header& hdr)
|
||||
{
|
||||
CMatrix* buffer = m_impl.getInputMatrix();
|
||||
buffer->copy(hdr.m_Header);
|
||||
|
||||
return m_impl.encodeHeader();
|
||||
}
|
||||
|
||||
template <>
|
||||
bool EncoderAdapter<TypeFeatureVector, Toolkit::TFeatureVectorEncoder<BoxAlgorithmProxy>>::encodeBufferImpl(
|
||||
const TypeFeatureVector::Buffer& buf)
|
||||
{
|
||||
CMatrix* buffer = m_impl.getInputMatrix();
|
||||
|
||||
buffer->copy(buf.m_buffer);
|
||||
|
||||
return m_impl.encodeBuffer();
|
||||
}
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "TypeMatrix.h"
|
||||
|
||||
#include "Decoder.h"
|
||||
#include "Encoder.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
template <>
|
||||
bool DecoderAdapter<TypeMatrix, Toolkit::TStreamedMatrixDecoder<BoxAlgorithmProxy>>::getHeaderImpl(TypeMatrix::Header& h)
|
||||
{
|
||||
CMatrix* decoded = m_impl.getOutputMatrix();
|
||||
h.m_Header.copy(*decoded);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
bool DecoderAdapter<TypeMatrix, Toolkit::TStreamedMatrixDecoder<BoxAlgorithmProxy>>::getBufferImpl(TypeMatrix::Buffer& b)
|
||||
{
|
||||
const CMatrix* decoded = m_impl.getOutputMatrix();
|
||||
b.m_buffer.copy(*decoded);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool EncoderAdapter<TypeMatrix, Toolkit::TStreamedMatrixEncoder<BoxAlgorithmProxy>>::encodeHeaderImpl(const TypeMatrix::Header& hdr)
|
||||
{
|
||||
CMatrix* header = m_impl.getInputMatrix();
|
||||
header->copy(hdr.m_Header);
|
||||
|
||||
return m_impl.encodeHeader();
|
||||
}
|
||||
|
||||
template <>
|
||||
bool EncoderAdapter<TypeMatrix, Toolkit::TStreamedMatrixEncoder<BoxAlgorithmProxy>>::encodeBufferImpl(const TypeMatrix::Buffer& buf)
|
||||
{
|
||||
CMatrix* buffer = m_impl.getInputMatrix();
|
||||
buffer->copy(buf.m_buffer);
|
||||
|
||||
return m_impl.encodeBuffer();
|
||||
}
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "TypeSignal.h"
|
||||
|
||||
#include "Decoder.h"
|
||||
#include "Encoder.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
template <>
|
||||
bool DecoderAdapter<TypeSignal, Toolkit::TSignalDecoder<BoxAlgorithmProxy>>::getHeaderImpl(TypeSignal::Header& h)
|
||||
{
|
||||
h.m_Sampling = m_impl.getOutputSamplingRate();
|
||||
|
||||
CMatrix* decoded = m_impl.getOutputMatrix();
|
||||
h.m_Header.copy(*decoded);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool DecoderAdapter<TypeSignal, Toolkit::TSignalDecoder<BoxAlgorithmProxy>>::getBufferImpl(TypeSignal::Buffer& b)
|
||||
{
|
||||
const CMatrix* decoded = m_impl.getOutputMatrix();
|
||||
b.m_buffer.copy(*decoded);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool EncoderAdapter<TypeSignal, Toolkit::TSignalEncoder<BoxAlgorithmProxy>>::encodeHeaderImpl(const TypeSignal::Header& hdr)
|
||||
{
|
||||
m_impl.getInputSamplingRate() = hdr.m_Sampling;
|
||||
|
||||
CMatrix* header = m_impl.getInputMatrix();
|
||||
header->copy(hdr.m_Header);
|
||||
|
||||
return m_impl.encodeHeader();
|
||||
}
|
||||
|
||||
template <>
|
||||
bool EncoderAdapter<TypeSignal, Toolkit::TSignalEncoder<BoxAlgorithmProxy>>::encodeBufferImpl(const TypeSignal::Buffer& buf)
|
||||
{
|
||||
CMatrix* buffer = m_impl.getInputMatrix();
|
||||
buffer->copy(buf.m_buffer);
|
||||
|
||||
return m_impl.encodeBuffer();
|
||||
}
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "TypeSpectrum.h"
|
||||
|
||||
#include "Decoder.h"
|
||||
#include "Encoder.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
template <>
|
||||
bool DecoderAdapter<TypeSpectrum, Toolkit::TSpectrumDecoder<BoxAlgorithmProxy>>::getHeaderImpl(TypeSpectrum::Header& h)
|
||||
{
|
||||
CMatrix* decoded = m_impl.getOutputMatrix();
|
||||
h.m_Header.copy(*decoded);
|
||||
|
||||
h.m_Sampling = m_impl.getOutputSamplingRate();
|
||||
|
||||
CMatrix* abscissas = m_impl.getOutputFrequencyAbscissa();
|
||||
h.m_Abscissas.copy(*abscissas);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool DecoderAdapter<TypeSpectrum, Toolkit::TSpectrumDecoder<BoxAlgorithmProxy>>::getBufferImpl(TypeSpectrum::Buffer& b)
|
||||
{
|
||||
const CMatrix* decoded = m_impl.getOutputMatrix();
|
||||
|
||||
b.m_buffer.copy(*decoded);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool EncoderAdapter<TypeSpectrum, Toolkit::TSpectrumEncoder<BoxAlgorithmProxy>>::encodeHeaderImpl(const TypeSpectrum::Header& hdr)
|
||||
{
|
||||
m_impl.getInputSamplingRate() = hdr.m_Sampling;
|
||||
|
||||
CMatrix* header = m_impl.getInputMatrix();
|
||||
header->copy(hdr.m_Header);
|
||||
|
||||
CMatrix* abscissas = m_impl.getInputFrequencyAbscissa();
|
||||
abscissas->copy(hdr.m_Abscissas);
|
||||
|
||||
return m_impl.encodeHeader();
|
||||
}
|
||||
|
||||
template <>
|
||||
bool EncoderAdapter<TypeSpectrum, Toolkit::TSpectrumEncoder<BoxAlgorithmProxy>>::encodeBufferImpl(const TypeSpectrum::Buffer& buf)
|
||||
{
|
||||
CMatrix* buffer = m_impl.getInputMatrix();
|
||||
buffer->copy(buf.m_buffer);
|
||||
|
||||
return m_impl.encodeBuffer();
|
||||
}
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+50
@@ -0,0 +1,50 @@
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include "TypeStimulation.h"
|
||||
|
||||
#include "Decoder.h"
|
||||
#include "Encoder.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
template <>
|
||||
bool DecoderAdapter<TypeStimulation, Toolkit::TStimulationDecoder<BoxAlgorithmProxy>>::getHeaderImpl(TypeStimulation::Header& /*target*/) { return true; }
|
||||
|
||||
template <>
|
||||
bool DecoderAdapter<TypeStimulation, Toolkit::TStimulationDecoder<BoxAlgorithmProxy>>::getBufferImpl(TypeStimulation::Buffer& b)
|
||||
{
|
||||
IStimulationSet* decoded = m_impl.getOutputStimulationSet();
|
||||
|
||||
b.m_buffer.clear();
|
||||
for (size_t i = 0; i < decoded->getStimulationCount(); ++i)
|
||||
{
|
||||
b.m_buffer.appendStimulation(decoded->getStimulationIdentifier(i), decoded->getStimulationDate(i), decoded->getStimulationDuration(i));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool EncoderAdapter<TypeStimulation, Toolkit::TStimulationEncoder<BoxAlgorithmProxy>>::encodeHeaderImpl(const TypeStimulation::Header& /*hdr*/)
|
||||
{
|
||||
return m_impl.encodeHeader();
|
||||
}
|
||||
|
||||
template <>
|
||||
bool EncoderAdapter<TypeStimulation, Toolkit::TStimulationEncoder<BoxAlgorithmProxy>>::encodeBufferImpl(const TypeStimulation::Buffer& buf)
|
||||
{
|
||||
IStimulationSet* inputSet = m_impl.getInputStimulationSet();
|
||||
|
||||
inputSet->clear();
|
||||
for (size_t i = 0; i < buf.m_buffer.getStimulationCount(); ++i)
|
||||
{
|
||||
inputSet->appendStimulation(buf.m_buffer.getStimulationIdentifier(i), buf.m_buffer.getStimulationDate(i) + m_offset.time(),
|
||||
buf.m_buffer.getStimulationDuration(i));
|
||||
}
|
||||
|
||||
return m_impl.encodeBuffer();
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
//
|
||||
// @note some ov files which have lots of stimulation chunks take long time to import
|
||||
// when launching tracker from visual studio. this is probably due to memory allocation, similar to slow simple dsp grammar parsing.
|
||||
//
|
||||
#include "Demuxer.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
#include "../../../../plugins/processing/file-io/src/ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
#include "Stream.h"
|
||||
|
||||
#include "CodecFactory.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
bool Demuxer::initialize()
|
||||
{
|
||||
// log() << Kernel::LogLevel_Info << "Source: Initializing with " << signalFile << "\n";
|
||||
|
||||
m_chunksSent = 0;
|
||||
m_pending = false;
|
||||
m_target.setSource(m_origin.getSource());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Demuxer::uninitialize() const
|
||||
{
|
||||
log() << Kernel::LogLevel_Info << "Source: Uninitializing\n";
|
||||
m_origin.uninitialize();
|
||||
return true;
|
||||
}
|
||||
|
||||
// bool Demuxer::pullChunk(MemoryBufferWithType& output)
|
||||
bool Demuxer::step()
|
||||
{
|
||||
// log() << Kernel::LogLevel_Info << "Source: Trying to pull a chunk\n";
|
||||
|
||||
while (!m_origin.isEOF())
|
||||
{
|
||||
while (!m_origin.isEOF() && m_reader.getCurrentNodeID() == EBML::CIdentifier())
|
||||
{
|
||||
std::vector<uint8_t> bytes;
|
||||
m_origin.read(bytes, 1);
|
||||
|
||||
//OV_ERROR_UNLESS_KRF(s == 1 || justStarted, "Unexpected EOF in " << m_filename, Kernel::ErrorType::BadParsing);
|
||||
if (!bytes.empty()) { m_reader.processData(&bytes[0], bytes.size()); }
|
||||
}
|
||||
if (!m_origin.isEOF() && m_reader.getCurrentNodeSize() != 0)
|
||||
{
|
||||
std::vector<uint8_t> bytes;
|
||||
m_origin.read(bytes, size_t(m_reader.getCurrentNodeSize()));
|
||||
|
||||
//OV_ERROR_UNLESS_KRF(s == m_swap.getSize(), "Unexpected EOF in " << m_filename, Kernel::ErrorType::BadParsing);
|
||||
|
||||
m_pendingChunk.m_Buffer.resize(0);
|
||||
m_pendingChunk.m_StartTime = CTime::max();
|
||||
m_pendingChunk.m_EndTime = CTime::max();
|
||||
m_pendingChunk.m_StreamIndex = std::numeric_limits<uint32_t>::max();
|
||||
|
||||
m_reader.processData(&bytes[0], bytes.size());
|
||||
}
|
||||
|
||||
if (m_pending)
|
||||
{
|
||||
// We have dada
|
||||
// log() << Kernel::LogLevel_Info << "Source: Found a chunk, queueing\n";
|
||||
const size_t streamIndex = m_pendingChunk.m_StreamIndex;
|
||||
|
||||
m_pending = false;
|
||||
|
||||
const StreamPtr stream = m_target.getStream(streamIndex);
|
||||
if (!stream)
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Error: Trying to decode stream without creating it first (buffer before header in EBML?)\n";
|
||||
return false;
|
||||
}
|
||||
m_decoders[streamIndex]->decode(m_pendingChunk);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_origin.isEOF()) { log() << Kernel::LogLevel_Trace << "Source file EOF reached\n"; }
|
||||
else { log() << Kernel::LogLevel_Warning << "Issue with source file\n"; }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Demuxer::isMasterChild(const EBML::CIdentifier& identifier)
|
||||
{
|
||||
if (identifier == EBML_Identifier_Header) { return true; }
|
||||
if (identifier == OVP_NodeId_OpenViBEStream_Header) { return true; }
|
||||
if (identifier == OVP_NodeId_OpenViBEStream_Header_Compression) { return false; }
|
||||
if (identifier == OVP_NodeId_OpenViBEStream_Header_StreamType) { return false; }
|
||||
if (identifier == OVP_NodeId_OpenViBEStream_Buffer) { return true; }
|
||||
if (identifier == OVP_NodeId_OpenViBEStream_Buffer_StreamIndex) { return false; }
|
||||
if (identifier == OVP_NodeId_OpenViBEStream_Buffer_StartTime) { return false; }
|
||||
if (identifier == OVP_NodeId_OpenViBEStream_Buffer_EndTime) { return false; }
|
||||
if (identifier == OVP_NodeId_OpenViBEStream_Buffer_Content) { return false; }
|
||||
return false;
|
||||
}
|
||||
|
||||
void Demuxer::openChild(const EBML::CIdentifier& identifier)
|
||||
{
|
||||
m_nodes.push(identifier);
|
||||
|
||||
EBML::CIdentifier& top = m_nodes.top();
|
||||
|
||||
if (top == EBML_Identifier_Header) { m_hasEBMLHeader = true; }
|
||||
if (top == OVP_NodeId_OpenViBEStream_Header)
|
||||
{
|
||||
if (!m_hasEBMLHeader)
|
||||
{
|
||||
//this->getLogManager() << Kernel::LogLevel_Info << "The file " << m_filename << " uses an outdated (but still compatible) version of the .ov file format\n";
|
||||
}
|
||||
}
|
||||
if (top == OVP_NodeId_OpenViBEStream_Header)
|
||||
{
|
||||
m_streamIdxToOutputIdxs.clear();
|
||||
m_streamIdxToTypeIDs.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void Demuxer::processChildData(const void* buffer, const size_t size)
|
||||
{
|
||||
EBML::CIdentifier& top = m_nodes.top();
|
||||
|
||||
// Uncomment this when ebml version will be used
|
||||
//if(top == EBML_Identifier_EBMLVersion) { const uint64_t versionNumber=(uint64_t)m_readerHelper.getUInt(buffer, size); }
|
||||
|
||||
if (top == OVP_NodeId_OpenViBEStream_Header_Compression)
|
||||
{
|
||||
//if (m_readerHelper.getUInt(buffer, size) != 0) { OV_WARNING_K("Impossible to use compression as it is not yet implemented"); }
|
||||
}
|
||||
else if (top == OVP_NodeId_OpenViBEStream_Header_StreamType)
|
||||
{
|
||||
const uint64_t typeID = m_readerHelper.getUInt(buffer, size);
|
||||
const size_t index = m_target.getNumStreams();
|
||||
m_target.createStream(index, typeID);
|
||||
DecoderBase* decoder = CodecFactory::getDecoder(m_kernelCtx, *m_target.getStream(index));
|
||||
m_decoders.push_back(decoder);
|
||||
}
|
||||
else if (top == OVP_NodeId_OpenViBEStream_Buffer_StreamIndex)
|
||||
{
|
||||
// @note if trying to do -1 to map to [0,...] convention, something breaks
|
||||
m_pendingChunk.m_StreamIndex = size_t(m_readerHelper.getUInt(buffer, size));
|
||||
//log() << Kernel::LogLevel_Info << "Run into index " << m_pendingChunk.streamIndex << "\n";
|
||||
}
|
||||
else if (top == OVP_NodeId_OpenViBEStream_Buffer_StartTime) { m_pendingChunk.m_StartTime = m_readerHelper.getUInt(buffer, size); }
|
||||
else if (top == OVP_NodeId_OpenViBEStream_Buffer_EndTime) { m_pendingChunk.m_EndTime = m_readerHelper.getUInt(buffer, size); }
|
||||
else if (top == OVP_NodeId_OpenViBEStream_Buffer_Content)
|
||||
{
|
||||
m_pendingChunk.m_Buffer.resize(size_t(size));
|
||||
memcpy(&m_pendingChunk.m_Buffer[0], reinterpret_cast<const uint8_t*>(buffer), size_t(size));
|
||||
}
|
||||
}
|
||||
|
||||
void Demuxer::closeChild()
|
||||
{
|
||||
EBML::CIdentifier& top = m_nodes.top();
|
||||
|
||||
if (top == OVP_NodeId_OpenViBEStream_Header) { } // Assign file streams to outputs here
|
||||
|
||||
if (top == OVP_NodeId_OpenViBEStream_Buffer)
|
||||
{
|
||||
m_pending = ((m_pendingChunk.m_StreamIndex != std::numeric_limits<size_t>::max()) &&
|
||||
(m_pendingChunk.m_StartTime != CTime::max()) && (m_pendingChunk.m_EndTime != CTime::max()));
|
||||
}
|
||||
|
||||
m_nodes.pop();
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
File diff suppressed because it is too large
Load Diff
Executable
+186
@@ -0,0 +1,186 @@
|
||||
#include <iostream>
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
#include "ParallelExecutor.h"
|
||||
#include <array>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
bool ParallelExecutor::initialize(const uint32_t nThreads)
|
||||
{
|
||||
m_quit = false;
|
||||
m_nJobsRunning = 0;
|
||||
m_jobList.clear();
|
||||
|
||||
// ExecutorView passes the working threads a few function handles to the executor that holds the state
|
||||
ExecutorView ctx(*this);
|
||||
|
||||
for (uint32_t i = 0; i < nThreads; ++i)
|
||||
{
|
||||
m_workerThreads.push_back(new CWorkerThread());
|
||||
m_threads.push_back(new std::thread(std::bind(&CWorkerThread::startWorkerThread, m_workerThreads[i], ctx, i)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParallelExecutor::uninitialize()
|
||||
{
|
||||
// Tell the threads waiting in the cond its time to quit, don't care of pending jobs
|
||||
{ // scope for lock
|
||||
std::unique_lock<std::mutex> lock(m_jobMutex);
|
||||
m_jobList.clear();
|
||||
m_quit = true;
|
||||
}
|
||||
|
||||
m_haveWork.notify_all();
|
||||
|
||||
for (uint32_t i = 0; i < m_threads.size(); ++i)
|
||||
{
|
||||
m_threads[i]->join();
|
||||
|
||||
delete m_threads[i];
|
||||
delete m_workerThreads[i];
|
||||
}
|
||||
|
||||
m_threads.clear();
|
||||
m_workerThreads.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParallelExecutor::pushJob(const jobCall& someJob)
|
||||
{
|
||||
// @fixme to add better concurrency, push a list instead; lock();add list;unlock();notify_all();
|
||||
{ // lock scope
|
||||
std::lock_guard<std::mutex> lock(m_jobMutex);
|
||||
m_jobList.push_back(someJob);
|
||||
}
|
||||
|
||||
m_haveWork.notify_one();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ParallelExecutor::pushJobList(const std::deque<jobCall>& vJobList)
|
||||
{
|
||||
{ // lock scope
|
||||
std::lock_guard<std::mutex> lock(m_jobMutex);
|
||||
|
||||
if (!m_jobList.empty())
|
||||
{
|
||||
std::cout << "Error, trying to push list with old jobs pending\n";
|
||||
return false;
|
||||
}
|
||||
m_jobList = vJobList;
|
||||
}
|
||||
|
||||
m_haveWork.notify_all();
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParallelExecutor::waitForAll()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_jobMutex);
|
||||
while (!m_jobList.empty()) { m_jobDone.wait(lock); }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParallelExecutor::getJob(jobCall& job)
|
||||
{
|
||||
// Wait until we get a job or are told to quit
|
||||
std::unique_lock<std::mutex> lock(m_jobMutex);
|
||||
|
||||
m_haveWork.wait(lock, [this]() { return (this->m_quit || !this->m_jobList.empty()); });
|
||||
|
||||
if (m_quit) { return false; }
|
||||
|
||||
// Ok, we have a job
|
||||
job = m_jobList.front();
|
||||
m_jobList.pop_front();
|
||||
|
||||
m_nJobsRunning++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParallelExecutor::declareDone()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_jobMutex);
|
||||
|
||||
m_nJobsRunning--;
|
||||
m_jobDone.notify_one();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParallelExecutor::clearPendingJobs()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_jobMutex);
|
||||
m_jobList.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t ParallelExecutor::getJobCount() const
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_jobMutex);
|
||||
return m_jobList.size();
|
||||
}
|
||||
|
||||
bool ParallelExecutor::isIdle() const
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_jobMutex);
|
||||
return (m_jobList.empty() && m_nJobsRunning == 0);
|
||||
}
|
||||
|
||||
|
||||
//___________________________________________________________________//
|
||||
// //
|
||||
|
||||
void testFunction(void* data)
|
||||
{
|
||||
for (uint32_t i = 0; i < 10; ++i)
|
||||
{
|
||||
std::cout << "Fun: " << *static_cast<uint32_t*>(data) << "\n";
|
||||
System::Time::sleep(*static_cast<uint32_t*>(data));
|
||||
}
|
||||
// return true;
|
||||
}
|
||||
|
||||
bool ParallelExecutor::launchTest()
|
||||
{
|
||||
std::array<int, 6> stuff = { 500, 666, 50, 1000, 300, 100 };
|
||||
|
||||
std::cout << "Push test\n";
|
||||
|
||||
this->pushJob(std::bind(testFunction, &stuff[0]));
|
||||
this->waitForAll();
|
||||
this->pushJob(std::bind(testFunction, &stuff[1]));
|
||||
this->pushJob(std::bind(testFunction, &stuff[2]));
|
||||
this->pushJob(std::bind(testFunction, &stuff[3]));
|
||||
this->pushJob(std::bind(testFunction, &stuff[4]));
|
||||
this->pushJob(std::bind(testFunction, &stuff[2]));
|
||||
this->waitForAll();
|
||||
this->waitForAll();
|
||||
|
||||
std::cout << "Pushlist test\n";
|
||||
|
||||
std::deque<jobCall> jobList;
|
||||
jobList.push_back(std::bind(testFunction, &stuff[0]));
|
||||
jobList.push_back(std::bind(testFunction, &stuff[1]));
|
||||
this->pushJobList(jobList);
|
||||
this->waitForAll();
|
||||
|
||||
std::cout << "Done\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+512
@@ -0,0 +1,512 @@
|
||||
#include "ProcExternalProcessing.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <deque>
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
#include <algorithm> // std::max
|
||||
|
||||
#include "CodecFactory.h"
|
||||
|
||||
// thread::
|
||||
// wait for connection;
|
||||
// while(connected ||!quit)
|
||||
// {
|
||||
// waitForChunk();
|
||||
// pushChunk();
|
||||
// }
|
||||
// if(quit) {
|
||||
// pushEndStim(); // XML have player controller that will quit
|
||||
// }
|
||||
// exit;
|
||||
|
||||
// #include "StreamChunk.h"
|
||||
// #include "StreamSignalChunk.h"
|
||||
// #include "StreamStimulationChunk.h"
|
||||
|
||||
#include "Stream.h"
|
||||
#include "TypeSignal.h"
|
||||
#include "TypeStimulation.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
void playerLaunch(const char* xmlFile, const char* args, bool playFast, bool noGUI, uint32_t identifier); // In processor.cpp
|
||||
|
||||
|
||||
ProcExternalProcessing::ProcExternalProcessing(const Kernel::IKernelContext& ctx, const ProcExternalProcessing& other) : Processor(ctx)
|
||||
{
|
||||
uint32_t sendPort, recvPort;
|
||||
other.getProcessorPorts(sendPort, recvPort);
|
||||
setProcessorPorts(sendPort, recvPort);
|
||||
|
||||
bool noGui, doSend, doReceive;
|
||||
other.getProcessorFlags(noGui, doSend, doReceive);
|
||||
setProcessorFlags(noGui, doSend, doReceive);
|
||||
|
||||
setArguments(other.getArguments());
|
||||
|
||||
initialize(other.getFilename());
|
||||
}
|
||||
|
||||
bool ProcExternalProcessing::initialize(const std::string& xmlFile)
|
||||
{
|
||||
m_pushStartTime = BufferedClient::CLIENT_NOT_STARTED;
|
||||
m_pullStartTime = BufferedClient::CLIENT_NOT_STARTED;
|
||||
|
||||
if (xmlFile.length() == 0)
|
||||
{
|
||||
// log() << Kernel::LogLevel_Trace << "No processor configured\n";
|
||||
m_xmlFilename.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
// log() << Kernel::LogLevel_Trace << "Processor: Initializing with " << xmlFile << "\n";
|
||||
|
||||
m_xmlFilename = xmlFile;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProcExternalProcessing::uninitialize()
|
||||
{
|
||||
// log() << Kernel::LogLevel_Trace << "Processor: Uninitializing\n";
|
||||
stop();
|
||||
|
||||
for (auto decoder : m_decoders) { delete decoder; }
|
||||
m_decoders.clear();
|
||||
for (auto encoder : m_encoders) { delete encoder; }
|
||||
m_encoders.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CTime ProcExternalProcessing::getCurrentTime() const
|
||||
{
|
||||
const CTime startTime = (m_doSend ? m_pushStartTime : m_pullStartTime);
|
||||
|
||||
if (startTime == BufferedClient::CLIENT_NOT_STARTED) { return CTime::min(); }
|
||||
|
||||
const CTime refTime = CTime(m_pushClient ? m_pushClient->getTime() : m_pullClient->getTime());
|
||||
const CTime currentTime = refTime - startTime;
|
||||
// log() << Kernel::LogLevel_Info << "Its " << CTime(currentTime).toSeconds() << "\n";
|
||||
|
||||
return currentTime;
|
||||
}
|
||||
|
||||
// If source track changes, we need a new codec since they are bound to the streams
|
||||
// @todo make streamless?
|
||||
bool ProcExternalProcessing::setNewSource(StreamBundle* source, const bool sendHeader, const bool sendEnd)
|
||||
{
|
||||
m_sendHeader = sendHeader;
|
||||
m_sendEnd = sendEnd;
|
||||
m_src = source;
|
||||
|
||||
for (auto encoder : m_encoders) { delete encoder; }
|
||||
m_encoders.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProcExternalProcessing::setNewTarget(StreamBundle* target)
|
||||
{
|
||||
m_dst = target;
|
||||
|
||||
for (auto decoder : m_decoders) { delete decoder; }
|
||||
m_decoders.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Sends all chunks up to the current time point t
|
||||
bool ProcExternalProcessing::push()
|
||||
{
|
||||
if (m_xmlFilename.length() == 0) { return false; }
|
||||
|
||||
if (!m_doSend)
|
||||
{
|
||||
// This processor is configured to receive, so we do nothing on send
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!m_pushClient || m_pushClient->hasQuit())
|
||||
{
|
||||
this->stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_src) { return false; }
|
||||
|
||||
if (m_pushStartTime == BufferedClient::CLIENT_NOT_STARTED)
|
||||
{
|
||||
m_pushStartTime = m_pushClient->getStartTime();
|
||||
if (m_pushStartTime == BufferedClient::CLIENT_NOT_STARTED)
|
||||
{
|
||||
// Not yet synced
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool foundSomething = false;
|
||||
m_sentSomething = false;
|
||||
|
||||
uint64_t chunksSent = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
size_t streamIndex;
|
||||
StreamPtr stream = m_src->getNextStream(streamIndex);
|
||||
|
||||
if (!stream) { break; }
|
||||
|
||||
if (m_encoders.size() <= streamIndex)
|
||||
{
|
||||
m_encoders.resize(streamIndex + 1, nullptr);
|
||||
m_encoders[streamIndex] = CodecFactory::getEncoder(m_kernelCtx, *stream);
|
||||
}
|
||||
|
||||
CTime chunkStartTime = CTime::min(), chunkEndTime = CTime::min();
|
||||
if (!stream->peek(chunkStartTime, chunkEndTime))
|
||||
{
|
||||
// if peek fails maybe the stream has ended?
|
||||
break;
|
||||
}
|
||||
|
||||
// There is something to send, now or later
|
||||
foundSomething = true;
|
||||
|
||||
const CTime elapsedTime = getCurrentTime();
|
||||
|
||||
// Send if its time
|
||||
if (elapsedTime >= chunkStartTime + m_previousEnd)
|
||||
{
|
||||
EncodedChunk chunk;
|
||||
EChunkType outputType;
|
||||
m_encoders[streamIndex]->setEncodeOffset(m_previousEnd);
|
||||
if (m_encoders[streamIndex]->encode(chunk, outputType))
|
||||
{
|
||||
const bool dontPush = (!m_sendHeader && outputType == EChunkType::Header) || (!m_sendEnd && outputType == EChunkType::End);
|
||||
|
||||
if (!dontPush)
|
||||
{
|
||||
chunk.m_StreamIndex = streamIndex;
|
||||
m_pushClient->pushBuffer(chunk);
|
||||
m_pushLastTime = chunk.m_EndTime;
|
||||
|
||||
/*
|
||||
log() << Kernel::LogLevel_Info << "Enc str " << streamIndex << " at " << CTime(elapsedTime).toSeconds()
|
||||
<< " chk [" << CTime(chunk.m_startTime).toSeconds()
|
||||
<< "," << CTime(chunk.m_endTime).toSeconds()
|
||||
<< "]\n";
|
||||
*/
|
||||
m_sentSomething = true;
|
||||
chunksSent++;
|
||||
}
|
||||
|
||||
stream->step();
|
||||
}
|
||||
else
|
||||
{
|
||||
// log() << Kernel::LogLevel_Error << "Error: Failed to encode chunk\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are early
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
break;
|
||||
}
|
||||
|
||||
// } while (sentSomething || m_PlayFast); // we loop until we have sent everything up to this moment.
|
||||
}
|
||||
|
||||
if (m_sentSomething)
|
||||
{
|
||||
//log() << Kernel::LogLevel_Info << "flush\n";
|
||||
m_pushClient->requestFlush();
|
||||
}
|
||||
|
||||
|
||||
// @fixme note that the while loop above may take long to return the control flow for some
|
||||
// bad streams
|
||||
|
||||
|
||||
if (!foundSomething)
|
||||
{
|
||||
// Maybe all streams ended?
|
||||
// log() << Kernel::LogLevel_Info << "Nothing to send - all streams ended?\n";
|
||||
|
||||
// Update time offset for 'continuous sending' mode
|
||||
// n.b. we can always do this, since for noncontinuous mode, new play() will be called, resetting these
|
||||
if (m_src->isFinished() && !m_requestNewOffset)
|
||||
{
|
||||
const CTime maxEndTime = m_src->getMaxDuration();
|
||||
m_previousEnd += maxEndTime; // Incremental for more than 2 tracks
|
||||
m_requestNewOffset = true;
|
||||
|
||||
// log() << Kernel::LogLevel_Info << "New offset at " << CTime(m_PreviousEnd).toSeconds() << "\n";
|
||||
}
|
||||
|
||||
// We do this to keep the External Processing box running on the designer side
|
||||
m_pushClient->requestFlush();
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_playFast)
|
||||
{
|
||||
// std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
// std::this_thread::yield();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sends all chunks up to the current time point t
|
||||
bool ProcExternalProcessing::pop()
|
||||
{
|
||||
if (m_xmlFilename.length() == 0) { return false; }
|
||||
|
||||
if (!m_doReceive)
|
||||
{
|
||||
// This processor is configured to send, so we do nothing on receive
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!m_pullClient || !m_dst) { return false; }
|
||||
|
||||
if (m_pullClient->hasQuit())
|
||||
{
|
||||
this->stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_pullStartTime == BufferedClient::CLIENT_NOT_STARTED)
|
||||
{
|
||||
m_pullStartTime = m_pullClient->getStartTime();
|
||||
if (m_pullStartTime == BufferedClient::CLIENT_NOT_STARTED)
|
||||
{
|
||||
// Not yet synced
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
EncodedChunk chunk;
|
||||
bool gotSomething = false;
|
||||
while (m_pullClient->pullBuffer(chunk))
|
||||
{
|
||||
// log() << Kernel::LogLevel_Info << "Got chunk " << chunk.m_startTime << "," << chunk.m_endTime << "\n";
|
||||
StreamPtr targetStream = m_dst->getStream(chunk.m_StreamIndex);
|
||||
if (!targetStream)
|
||||
{
|
||||
if (!m_dst->createStream(chunk.m_StreamIndex, chunk.m_StreamType)) { continue; }
|
||||
targetStream = m_dst->getStream(chunk.m_StreamIndex);
|
||||
if (m_decoders.size() <= chunk.m_StreamIndex) { m_decoders.resize(chunk.m_StreamIndex + 1, nullptr); }
|
||||
m_decoders[chunk.m_StreamIndex] = CodecFactory::getDecoder(m_kernelCtx, *targetStream);
|
||||
}
|
||||
|
||||
// @note Decoder doesn't need to add offsets as these times are coming from the
|
||||
// external processor, so they are already offset by the past length.
|
||||
if (!chunk.m_Buffer.empty())
|
||||
{
|
||||
m_decoders[chunk.m_StreamIndex]->decode(chunk);
|
||||
|
||||
/*
|
||||
log() << Kernel::LogLevel_Info << "Dec str " << chunk.streamIndex << " chk [" << CTime(chunk.m_startTime).toSeconds()
|
||||
<< "," << CTime(chunk.m_endTime).toSeconds() << "]\n";
|
||||
*/
|
||||
}
|
||||
|
||||
m_pullLastTime = chunk.m_EndTime;
|
||||
|
||||
gotSomething = true;
|
||||
}
|
||||
|
||||
if (!gotSomething) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); }
|
||||
|
||||
if (m_pullClient->hasQuit()) { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProcExternalProcessing::play(const bool playFast, const std::function<bool(CTime)>& quitCB, const std::function<bool()>& nextTrackFun)
|
||||
{
|
||||
m_pushLastTime = CTime::min();
|
||||
m_pullLastTime = CTime::min();
|
||||
m_isRunning = false;
|
||||
|
||||
if (m_xmlFilename.length() == 0)
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Error: No processor initialized\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_chunksSent = 0;
|
||||
// m_PreviousChunkEnd = 0;
|
||||
m_playFast = playFast; // @todo To work neatly it'd be better to be able to pass in the chunk times to the designer side
|
||||
m_previousEnd = 0;
|
||||
m_requestNewOffset = false;
|
||||
|
||||
for (auto decoder : m_decoders) { delete decoder; }
|
||||
m_decoders.clear();
|
||||
for (auto encoder : m_encoders) { delete encoder; }
|
||||
m_encoders.clear();
|
||||
|
||||
// log() << Kernel::LogLevel_Info << "Reset offset to " << CTime(m_PreviousEnd).toSeconds() << "\n";
|
||||
|
||||
const CString expandedName = m_kernelCtx.getConfigurationManager().expand(m_xmlFilename.c_str());
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "--define Tracker_Port_Send " << m_sendPort << " ";
|
||||
ss << "--define Tracker_Port_Receive " << m_recvPort << " ";
|
||||
|
||||
const std::string allArgs = m_arguments + " " + ss.str();
|
||||
|
||||
m_playerThread = new std::thread(std::bind(&playerLaunch, expandedName, allArgs.c_str(), m_playFast, m_noGUI, m_sendPort));
|
||||
|
||||
// @fixme this ad-hoc sleep is pretty terrible, but it seems that if player launch is slow enough and
|
||||
// we launch the client threads immediately below, in Windows it results in deadlocks and errors, suggesting that
|
||||
// the concurrency control logic is not really correct in the communication with the external processing box.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||
|
||||
if (m_doSend)
|
||||
{
|
||||
m_pushClient = new PushClient(m_sendPort);
|
||||
m_pushClientThread = new std::thread(&PushClient::start, m_pushClient);
|
||||
}
|
||||
if (m_doReceive)
|
||||
{
|
||||
m_pullClient = new PullClient(m_recvPort);
|
||||
m_pullClientThread = new std::thread(&PullClient::start, m_pullClient);
|
||||
}
|
||||
|
||||
// log() << Kernel::LogLevel_Debug << "External processing thread(s) launched, waiting sync\n";
|
||||
|
||||
for (size_t retries = 0; retries < 20; ++retries)
|
||||
{
|
||||
if (m_doSend) { m_pushStartTime = m_pushClient->getStartTime(); }
|
||||
if (m_doReceive) { m_pullStartTime = m_pullClient->getStartTime(); }
|
||||
if (isSynced())
|
||||
{
|
||||
// log() << Kernel::LogLevel_Debug << "Clients synced after " << retries << " retries\n";
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
}
|
||||
if (!isSynced())
|
||||
{
|
||||
// log() << Kernel::LogLevel_Error << "Error syncing client(s) after 10 secs of retries\n";
|
||||
if (m_pushClient) { m_pushClient->requestQuit(); }
|
||||
if (m_pullClient) { m_pullClient->requestQuit(); }
|
||||
return false;
|
||||
}
|
||||
|
||||
m_isRunning = true;
|
||||
while (m_isRunning)
|
||||
{
|
||||
m_isRunning &= push();
|
||||
m_isRunning &= pop();
|
||||
|
||||
if (!m_isRunning && nextTrackFun) { m_isRunning = nextTrackFun(); }
|
||||
// @fixme the quit callback might be a bit expensive, shouldn't hammer it
|
||||
if (quitCB && quitCB((m_doSend ? m_pushLastTime : m_pullLastTime))) { m_isRunning = false; }
|
||||
}
|
||||
|
||||
stop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProcExternalProcessing::stop()
|
||||
{
|
||||
m_isRunning = false;
|
||||
|
||||
if (m_pushClient)
|
||||
{
|
||||
// log() << Kernel::LogLevel_Info << "Stopping external processing push client\n";
|
||||
|
||||
m_pushClient->requestQuit();
|
||||
if (m_pushClientThread) { m_pushClientThread->join(); }
|
||||
delete m_pushClientThread;
|
||||
m_pushClientThread = nullptr;
|
||||
delete m_pushClient;
|
||||
m_pushClient = nullptr;
|
||||
}
|
||||
|
||||
if (m_pullClient)
|
||||
{
|
||||
// log() << Kernel::LogLevel_Info << "Stopping external processing pull client\n";
|
||||
|
||||
m_pullClient->requestQuit();
|
||||
if (m_pullClientThread) { m_pullClientThread->join(); }
|
||||
delete m_pullClientThread;
|
||||
m_pullClientThread = nullptr;
|
||||
delete m_pullClient;
|
||||
m_pullClient = nullptr;
|
||||
}
|
||||
|
||||
// tear down the player object
|
||||
if (m_playerThread)
|
||||
{
|
||||
// log() << Kernel::LogLevel_Trace << "Joining player thread\n";
|
||||
m_playerThread->join();
|
||||
delete m_playerThread;
|
||||
m_playerThread = nullptr;
|
||||
}
|
||||
|
||||
m_pushStartTime = BufferedClient::CLIENT_NOT_STARTED;
|
||||
m_pullStartTime = BufferedClient::CLIENT_NOT_STARTED;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProcExternalProcessing::save()
|
||||
{
|
||||
auto& mgr = m_kernelCtx.getConfigurationManager();
|
||||
|
||||
std::stringstream sPort;
|
||||
sPort << m_sendPort;
|
||||
// std::stringstream rPort; rPort << m_RecvPort;
|
||||
|
||||
mgr.addOrReplaceConfigurationToken("Tracker_Workspace_Processor", m_xmlFilename.c_str());
|
||||
mgr.addOrReplaceConfigurationToken("Tracker_Workspace_Processor_FirstPort", sPort.str().c_str());
|
||||
// mgr.addOrReplaceConfigurationToken("Tracker_Workspace_Processor_RecvPort", rPort.str().c_str());
|
||||
mgr.addOrReplaceConfigurationToken("Tracker_Workspace_Processor_NoGUI", (m_noGUI ? "true" : "false"));
|
||||
mgr.addOrReplaceConfigurationToken("Tracker_Workspace_Processor_DoSend", (m_doSend ? "true" : "false"));
|
||||
mgr.addOrReplaceConfigurationToken("Tracker_Workspace_Processor_DoReceive", (m_doReceive ? "true" : "false"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProcExternalProcessing::load()
|
||||
{
|
||||
auto& mgr = m_kernelCtx.getConfigurationManager();
|
||||
|
||||
if (mgr.lookUpConfigurationTokenIdentifier("Tracker_Workspace_Processor") != CIdentifier::undefined())
|
||||
{
|
||||
initialize(mgr.lookUpConfigurationTokenValue("Tracker_Workspace_Processor").toASCIIString());
|
||||
}
|
||||
|
||||
if (mgr.lookUpConfigurationTokenIdentifier("Tracker_Workspace_Processor_FirstPort") != CIdentifier::undefined())
|
||||
{
|
||||
const uint32_t port = uint32_t(mgr.expandAsUInteger("${Tracker_Workspace_Processor_FirstPort}"));
|
||||
setProcessorPorts(port, port + 1);
|
||||
}
|
||||
|
||||
m_noGUI = mgr.expandAsBoolean("${Tracker_Workspace_Processor_NoGUI}", m_noGUI);
|
||||
m_doSend = mgr.expandAsBoolean("${Tracker_Workspace_Processor_DoSend}", m_doSend);
|
||||
m_doReceive = mgr.expandAsBoolean("${Tracker_Workspace_Processor_DoReceive}", m_doReceive);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <sstream>
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
#include "ProcExternalProcessingHelper.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
const CTime BufferedClient::CLIENT_NOT_STARTED = CTime::max();
|
||||
|
||||
void BufferedClient::requestQuit()
|
||||
{
|
||||
std::lock_guard<std::mutex> oLock(m_threadMutex);
|
||||
m_pleaseQuit = true;
|
||||
m_bufferCondition.notify_one();
|
||||
}
|
||||
|
||||
bool BufferedClient::hasQuit()
|
||||
{
|
||||
std::lock_guard<std::mutex> oLock(m_threadMutex);
|
||||
return m_hasQuit;
|
||||
}
|
||||
|
||||
void BufferedClient::start()
|
||||
{
|
||||
if (!connectClient())
|
||||
{
|
||||
std::lock_guard<std::mutex> oLock(m_threadMutex);
|
||||
m_hasQuit = true;
|
||||
return;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Push or pull, uses lock internally
|
||||
if (!step()) { break; }
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> oLock(m_threadMutex);
|
||||
|
||||
// Exit the loop if we're told to quit or if we've lost the connection
|
||||
if (m_pleaseQuit || !isConnected() || isEndReceived()) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
// Shut the connection
|
||||
this->close();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> oLock(m_threadMutex);
|
||||
m_hasQuit = true;
|
||||
}
|
||||
|
||||
// The thread will exit here and can be joined
|
||||
}
|
||||
|
||||
bool BufferedClient::connectClient()
|
||||
{
|
||||
static int connectId = 0;
|
||||
|
||||
std::stringstream cId;
|
||||
cId << std::string("tracker");
|
||||
cId << connectId++;
|
||||
|
||||
const std::string connectionID = cId.str();
|
||||
this->setConnectionID(connectionID);
|
||||
int errorCount = 0;
|
||||
while (!this->connect("127.0.0.1", m_port))
|
||||
{
|
||||
const ELibraryError error = this->getLastError();
|
||||
|
||||
if (error == Socket_FailedToConnect)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
if (errorCount++ > 20)
|
||||
{
|
||||
std::cout << "Designer not responding on port " << m_port << " retried 5 secs" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error " << error << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
//if (s_DidRequestForcedQuit) { exit(EXIT_SUCCESS); }
|
||||
}
|
||||
|
||||
// Announce to server that the box has finished initializing and wait for acknowledgement
|
||||
errorCount = 0;
|
||||
while (!this->waitForSyncMessage())
|
||||
{
|
||||
if (errorCount++ > 10)
|
||||
{
|
||||
std::cout << "Server not syncing in port " << m_port << std::endl;
|
||||
this->close();
|
||||
return false;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
}
|
||||
this->pushLog(Communication::ELogLevel::LogLevel_Info, "Received Ping");
|
||||
|
||||
this->pushSync();
|
||||
this->pushLog(Communication::ELogLevel::LogLevel_Info, "Sent Pong");
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> oLock(m_threadMutex);
|
||||
m_startTime = this->getTime();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PushClient::pushBuffer(const EncodedChunk& encodedChunk)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> oLock(m_threadMutex);
|
||||
if (!m_pleaseQuit)
|
||||
{
|
||||
EncodedChunk* buffer = new EncodedChunk(encodedChunk);
|
||||
m_buffer.push_back(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// No big harm notifying in any case, though if in 'quit' state, the quit request has already notified
|
||||
m_bufferCondition.notify_one();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PushClient::step()
|
||||
{
|
||||
std::unique_lock<std::mutex> oLock(m_threadMutex, std::defer_lock);
|
||||
|
||||
oLock.lock();
|
||||
|
||||
// Normal condition for the wait to exit is the flush request or having more data
|
||||
m_bufferCondition.wait(oLock, [this]() { return (m_pleaseFlush || !m_buffer.empty() || m_pleaseQuit || !isConnected() || isEndReceived()); });
|
||||
|
||||
while (!m_buffer.empty())
|
||||
{
|
||||
EncodedChunk* chunk = m_buffer[0];
|
||||
m_buffer.pop_front();
|
||||
|
||||
if (!this->pushEBML(chunk->m_StreamIndex, chunk->m_StartTime.time(), chunk->m_EndTime.time(),
|
||||
std::make_shared<const std::vector<uint8_t>>(chunk->m_Buffer)))
|
||||
{
|
||||
std::cerr << "Failed to push EBML.\n";
|
||||
std::cerr << "Error " << this->getLastError() << "\n";
|
||||
oLock.unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
delete chunk;
|
||||
}
|
||||
|
||||
if (m_pleaseFlush)
|
||||
{
|
||||
this->pushSync();
|
||||
m_pleaseFlush = false;
|
||||
|
||||
// We don't use condition variable here as users of the client should never
|
||||
// be able to interrupt this wait unless error state is reached
|
||||
while (!m_pleaseQuit && isConnected() && !isEndReceived() && !this->waitForSyncMessage())
|
||||
{
|
||||
oLock.unlock();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
oLock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
oLock.unlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PushClient::requestFlush()
|
||||
{
|
||||
std::lock_guard<std::mutex> oLock(m_threadMutex);
|
||||
m_pleaseFlush = true;
|
||||
m_bufferCondition.notify_one();
|
||||
}
|
||||
|
||||
bool PullClient::pullBuffer(EncodedChunk& chunk)
|
||||
{
|
||||
std::lock_guard<std::mutex> oLock(m_threadMutex);
|
||||
|
||||
if (m_buffer.empty()) { return false; }
|
||||
|
||||
EncodedChunk* ptr = m_buffer[0];
|
||||
m_buffer.pop_front();
|
||||
chunk = *ptr;
|
||||
delete ptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PullClient::popMessagesToBuffer()
|
||||
{
|
||||
uint64_t packetId, startTime, endTime;
|
||||
size_t streamIndex;
|
||||
std::shared_ptr<const std::vector<uint8_t>> ebml;
|
||||
bool gotSomething = false;
|
||||
|
||||
while (this->popEBML(packetId, streamIndex, startTime, endTime, ebml))
|
||||
{
|
||||
// @todo optimize by passing in the buffer to popEBML already
|
||||
EncodedChunk* chunk = new EncodedChunk();
|
||||
|
||||
chunk->m_StartTime = startTime;
|
||||
chunk->m_EndTime = endTime;
|
||||
chunk->m_StreamIndex = streamIndex;
|
||||
|
||||
// @fixme inefficient to query for each chunk since its stream specific
|
||||
uint64_t id;
|
||||
std::string streamName;
|
||||
this->getInput(chunk->m_StreamIndex, id, chunk->m_StreamType, streamName);
|
||||
|
||||
chunk->m_Buffer.resize(ebml->size());
|
||||
for (size_t i = 0; i < ebml->size(); ++i) { chunk->m_Buffer[i] = (*ebml)[i]; }
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> oLock(m_threadMutex);
|
||||
m_buffer.push_back(chunk);
|
||||
}
|
||||
|
||||
gotSomething = true;
|
||||
|
||||
//std::cout << "Got pkg " << packetId << " idx " << streamIndex << " siz " << chunk.bufferData.size() << "\n";
|
||||
}
|
||||
|
||||
return gotSomething;
|
||||
}
|
||||
|
||||
// pull
|
||||
bool PullClient::step()
|
||||
{
|
||||
// Pull items until we get the sync message (no more to send)
|
||||
while (!this->waitForSyncMessage() && this->isConnected() && !this->isEndReceived())
|
||||
{
|
||||
popMessagesToBuffer();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
// std::this_thread::yield();
|
||||
}
|
||||
|
||||
// Got sync, pull whatever remaining buffered on the sender
|
||||
popMessagesToBuffer();
|
||||
|
||||
if (!this->isConnected() || this->isEndReceived()) { return false; }
|
||||
|
||||
// Notify we've processed everything
|
||||
this->pushSync();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
#include "Processor.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
|
||||
#include <fs/Files.h>
|
||||
#include <system/ovCMath.h>
|
||||
#include <thread>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
void playerLaunch(const char* xmlFile, const char* args, bool playFast, bool noGUI, uint32_t identifier)
|
||||
{
|
||||
std::string designer = std::string(Directories::getBinDir().toASCIIString()) + "/openvibe-designer.exe";
|
||||
std::string argsAll = std::string(" --no-session-management ") + (args ? args : "") + (noGUI ? "--no-gui " : "") + (playFast ? "--play-fast " : " --play ")
|
||||
+ " " + "\"" + xmlFile + "\"";
|
||||
if (!FS::Files::directoryExists(Directories::getUserDataDir().toASCIIString())) { FS::Files::createPath(Directories::getUserDataDir().toASCIIString()); }
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::string(Directories::getLogDir().toASCIIString()) << "/tracker-processor-dump-" << identifier << ".txt";
|
||||
std::string outputDump = ss.str();
|
||||
|
||||
std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
||||
std::ofstream outStream(outputDump.c_str(), std::ios::app);
|
||||
outStream << std::endl << "Date of launch: " << std::ctime(&now) << std::endl;
|
||||
outStream << "Trying to launch: " << designer << std::endl;
|
||||
outStream << "Args: " << argsAll << std::endl;
|
||||
outStream << "Logging to: " << outputDump << std::endl;
|
||||
outStream << std::endl;
|
||||
outStream.close();
|
||||
|
||||
#if TARGET_OS_Windows
|
||||
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
|
||||
// set the size of the structures
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
SECURITY_ATTRIBUTES sa;
|
||||
sa.nLength = sizeof(sa);
|
||||
sa.lpSecurityDescriptor = nullptr;
|
||||
sa.bInheritHandle = TRUE;
|
||||
|
||||
HANDLE h = CreateFile(outputDump.c_str(), FILE_APPEND_DATA, FILE_SHARE_WRITE | FILE_SHARE_READ, &sa, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
|
||||
si.dwFlags |= STARTF_USESTDHANDLES;
|
||||
si.hStdInput = nullptr;
|
||||
si.hStdError = h;
|
||||
si.hStdOutput = h;
|
||||
|
||||
argsAll = std::string("\"") + designer + std::string("\"") + std::string(" ") + argsAll;
|
||||
|
||||
LPSTR argsLp = const_cast<char*>(argsAll.c_str());
|
||||
|
||||
BOOL retVal = CreateProcess(designer.c_str(), // the path
|
||||
argsLp, // Command line
|
||||
nullptr, // Process handle not inheritable
|
||||
nullptr, // Thread handle not inheritable
|
||||
TRUE, // Set handle inheritance to FALSE
|
||||
0, // No creation flags
|
||||
nullptr, // Use parent's environment block
|
||||
nullptr, // Use parent's starting directory
|
||||
&si, // Pointer to STARTUPINFO structure
|
||||
&pi // Pointer to PROCESS_INFORMATION structure (removed extra parentheses)
|
||||
);
|
||||
// Close process and thread handles.
|
||||
if (!retVal) { std::cout << "err: " << GetLastError() << "\n"; }
|
||||
else { WaitForSingleObject(pi.hProcess, INFINITE); }
|
||||
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(h);
|
||||
#else
|
||||
|
||||
auto cmd = designer + argsAll + " >" + outputDump;
|
||||
|
||||
// std::cout << "Call is: " << cmd << "\n";
|
||||
|
||||
if (system(cmd.c_str()) != 0)
|
||||
{
|
||||
std::cout << "Launch of [" << cmd << "] failed\n";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Processor::configure(const char* filename)
|
||||
{
|
||||
const std::string usedFilename = (filename ? filename : m_xmlFilename.c_str());
|
||||
|
||||
if (usedFilename.length() == 0)
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Error: Please set processor filename first\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
const CString expandedName = m_kernelCtx.getConfigurationManager().expand(usedFilename.c_str());
|
||||
|
||||
const std::string designer = std::string(Directories::getBinDir().toASCIIString()) + "/openvibe-designer --no-session-management --open ";
|
||||
const std::string outputDump = std::string(Directories::getDistRootDir().toASCIIString()) + "/tracker-processor-configure-dump.txt";
|
||||
const std::string cmd = designer + expandedName.toASCIIString() + " >" + outputDump;
|
||||
|
||||
if (system(cmd.c_str()) != 0)
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Launch of [" << cmd << "] failed\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
// @author J.T. Lindgren
|
||||
//
|
||||
|
||||
#include "Selection.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
bool Selection::reset(const bool state) const
|
||||
{
|
||||
for (auto& t : m_tracks) { for (auto& s : t->getAllStreams()) { s->setSelected(state); } }
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Selection::isEmpty() const
|
||||
{
|
||||
for (const auto& t : m_tracks) { if (t->getNumStreams() > 0) { return false; } }
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Selection::isSomethingSelected() const
|
||||
{
|
||||
for (const auto& t : m_tracks) { for (const auto& str : t->getAllStreams()) { if (str->getSelected()) { return true; } } }
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Selection::isTrackSelected(const size_t track) const
|
||||
{
|
||||
if (track >= m_tracks.size()) { return false; }
|
||||
for (const auto& s : m_tracks[track]->getAllStreams()) { if (s->getSelected()) { return true; } }
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Selection::isSelectionConsistent() const
|
||||
{
|
||||
if (isEmpty() || !isSomethingSelected()) { return true; }
|
||||
|
||||
std::vector<CIdentifier> selectedTypesPrevious;
|
||||
|
||||
// Check that selection for each track is identical to selection of previous track
|
||||
// (empty selections per track are ignored)
|
||||
for (auto& t : m_tracks)
|
||||
{
|
||||
std::vector<CIdentifier> selectedTypes;
|
||||
for (const auto& s : t->getAllStreams()) { if (s->getSelected()) { selectedTypes.push_back(s->getTypeIdentifier()); } }
|
||||
if (!selectedTypesPrevious.empty() && !selectedTypes.empty() && selectedTypesPrevious != selectedTypes) { return false; }
|
||||
if (!selectedTypes.empty()) { selectedTypesPrevious = selectedTypes; }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Selection::countSelectedTracks() const
|
||||
{
|
||||
size_t selectedTracks = 0;
|
||||
|
||||
for (const auto& t : m_tracks)
|
||||
{
|
||||
for (const auto& s : t->getAllStreams())
|
||||
{
|
||||
if (s->getSelected())
|
||||
{
|
||||
selectedTracks++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return selectedTracks;
|
||||
}
|
||||
|
||||
size_t Selection::countSelectedStreams(const size_t trackIndex) const
|
||||
{
|
||||
if (trackIndex >= m_tracks.size()) { return 0; }
|
||||
|
||||
size_t numSelected = 0;
|
||||
for (const auto& str : m_tracks[trackIndex]->getAllStreams()) { if (str && str->getSelected()) { numSelected++; } }
|
||||
return numSelected;
|
||||
}
|
||||
|
||||
bool Selection::save(const char* prefix) const
|
||||
{
|
||||
auto& mgr = this->getKernelContext().getConfigurationManager();
|
||||
|
||||
// @note with really huge datasets the .ovw file handling can get slow. should be profiled.
|
||||
for (size_t t = 0; t < m_tracks.size(); ++t)
|
||||
{
|
||||
std::stringstream token;
|
||||
token << prefix << "Track_" << std::setw(3) << std::setfill('0') << (t + 1) << "_Selected";
|
||||
std::stringstream value;
|
||||
|
||||
for (size_t s = 0; s < m_tracks[t]->getNumStreams(); ++s) { if (m_tracks[t]->getStream(s)->getSelected()) { value << (s + 1) << " "; } }
|
||||
|
||||
mgr.addOrReplaceConfigurationToken(token.str().c_str(), value.str().c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Relies on the current track/stream set being compatible with the tokens. no error checking
|
||||
bool Selection::load(const char* prefix) const
|
||||
{
|
||||
auto& mgr = this->getKernelContext().getConfigurationManager();
|
||||
|
||||
for (size_t t = 0; t < m_tracks.size(); ++t)
|
||||
{
|
||||
std::stringstream token;
|
||||
token << prefix << "Track_" << std::setw(3) << std::setfill('0') << (t + 1) << "_Selected";
|
||||
|
||||
std::stringstream value(mgr.lookUpConfigurationTokenValue(token.str().c_str()).toASCIIString());
|
||||
|
||||
// Mark everything as unselected by default
|
||||
for (size_t s = 0; s < m_tracks[t]->getNumStreams(); ++s) { m_tracks[t]->getStream(s)->setSelected(false); }
|
||||
|
||||
// Load sparse selection from file
|
||||
std::string selected;
|
||||
while (std::getline(value, selected, ' '))
|
||||
{
|
||||
const uint32_t selectedIdx = atoi(selected.c_str()) - 1;
|
||||
if (selectedIdx < m_tracks[t]->getNumStreams()) { m_tracks[t]->getStream(selectedIdx)->setSelected(true); }
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+70
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
// @author J.T. Lindgren
|
||||
//
|
||||
|
||||
#include "Stream.h"
|
||||
#include "StimulationStreamFilter.h"
|
||||
|
||||
#include "TypeStimulation.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
// @note At some point I thought to make a derived class of stimulation stream that simply wouldn't pass through
|
||||
// the filtered stimulations on calling the getChunk(), but this resulted in some issues. Since modifying
|
||||
// the original stream would not have been appropriate, I'd have had to choose one of the following,
|
||||
// 1) make getChunk() in general copy all data instead of returning pointers 2) drop const qualifier
|
||||
// from the getChunk() to keep track of allocated memory internally in the derived class or 3) make getChunk()
|
||||
// return a smart pointer. I wasn't very happy about these options, so instead we just make a modded copy of the stream.
|
||||
std::shared_ptr<StreamBase> filterStimulationStreamEndPoints(const std::shared_ptr<const StreamBase>& src, const Kernel::IKernelContext& ctx)
|
||||
{
|
||||
const std::vector<uint64_t> stims = { OVTK_StimulationId_ExperimentStop, OVTK_StimulationId_EndOfFile, OVTK_GDF_End_Of_Session };
|
||||
|
||||
auto result = filterStimulationStream(src, ctx, stims);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<StreamBase> filterStimulationStream(const std::shared_ptr<const StreamBase>& src, const Kernel::IKernelContext& ctx,
|
||||
const std::vector<uint64_t>& stimsToFilter)
|
||||
{
|
||||
if (src->getTypeIdentifier() != OV_TypeId_Stimulations) { return nullptr; }
|
||||
|
||||
const auto typedSrc = std::static_pointer_cast<const Stream<TypeStimulation>>(src);
|
||||
auto target = std::make_shared<Stream<TypeStimulation>>(ctx);
|
||||
|
||||
target->clear();
|
||||
|
||||
target->getHeader().m_StartTime = typedSrc->getHeader().m_StartTime;
|
||||
target->getHeader().m_EndTime = typedSrc->getHeader().m_EndTime;
|
||||
|
||||
for (size_t chk = 0; chk < typedSrc->getChunkCount(); ++chk)
|
||||
{
|
||||
const auto chunk = typedSrc->getChunk(chk);
|
||||
auto newChunk = new TypeStimulation::Buffer;
|
||||
|
||||
for (size_t i = 0; i < chunk->m_buffer.getStimulationCount(); ++i)
|
||||
{
|
||||
const uint64_t id = chunk->m_buffer.getStimulationIdentifier(i);
|
||||
if (std::none_of(stimsToFilter.begin(), stimsToFilter.end(), [id](const uint64_t val) { return val == id; }))
|
||||
{
|
||||
const uint64_t timestamp = chunk->m_buffer.getStimulationDate(i);
|
||||
const uint64_t duration = chunk->m_buffer.getStimulationDuration(i);
|
||||
newChunk->m_buffer.appendStimulation(id, timestamp, duration);
|
||||
}
|
||||
}
|
||||
|
||||
newChunk->m_StartTime = chunk->m_StartTime;
|
||||
newChunk->m_EndTime = chunk->m_EndTime;
|
||||
|
||||
target->push(newChunk);
|
||||
}
|
||||
|
||||
target->getEnd().m_StartTime = typedSrc->getEnd().m_StartTime;
|
||||
target->getEnd().m_EndTime = typedSrc->getEnd().m_EndTime;
|
||||
|
||||
return target;
|
||||
}
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
// @author J.T. Lindgren
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "StreamBundle.h"
|
||||
|
||||
#include "Stream.h"
|
||||
|
||||
#include "StreamFactory.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
bool StreamBundle::deepCopy(const StreamBundle& other)
|
||||
{
|
||||
bool retVal = true;
|
||||
|
||||
// Clear streams
|
||||
initialize();
|
||||
|
||||
// Copy each stream
|
||||
for (size_t i = 0; i < other.getNumStreams(); ++i)
|
||||
{
|
||||
retVal &= createStream(i, other.getStream(i)->getTypeIdentifier());
|
||||
retVal &= getStream(i)->copy(*other.getStream(i));
|
||||
}
|
||||
|
||||
m_source = other.getSource();
|
||||
setDirtyBit(other.getDirtyBit());
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
bool StreamBundle::copyFrom(StreamBundle& other)
|
||||
{
|
||||
initialize();
|
||||
|
||||
for (const auto& str : other.getAllStreams()) { if (str->getSelected()) { this->setStream(m_streams.size(), str); } }
|
||||
|
||||
setSource(other.getSource());
|
||||
setDirtyBit(other.getDirtyBit());
|
||||
|
||||
// n.b. this will affect the streams we copied
|
||||
rewind();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamBundle::initialize()
|
||||
{
|
||||
//reset first
|
||||
uninitialize();
|
||||
m_dirty = true;
|
||||
// log() << Kernel::LogLevel_Debug << "Streams initialized ok\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamBundle::uninitialize()
|
||||
{
|
||||
// Since m_Streams are shared pointers, no need to delete them
|
||||
m_streams.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamBundle::rewind()
|
||||
{
|
||||
bool returnValue = true;
|
||||
std::for_each(m_streams.begin(), m_streams.end(), [&returnValue](const StreamPtr& entry) { if (entry) { returnValue &= entry->reset(); } });
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
bool StreamBundle::createStream(const size_t index, const CIdentifier& typeID)
|
||||
{
|
||||
if (index >= m_streams.size()) { m_streams.resize(index + 1, nullptr); }
|
||||
|
||||
if (m_streams[index] == nullptr)
|
||||
{
|
||||
const StreamPtr stream = StreamFactory::getStream(m_kernelCtx, typeID);
|
||||
if (!stream) { return false; }
|
||||
m_streams[index] = stream;
|
||||
setDirtyBit(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
log() << Kernel::LogLevel_Error << "Error: Slot " << index << " is already used\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StreamBundle::deleteStream(const size_t index)
|
||||
{
|
||||
if (index > m_streams.size())
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Error: Stream index exceeds array size\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// m_Streams is shared ptrs, no need to delete
|
||||
m_streams.erase(m_streams.begin() + index);
|
||||
setDirtyBit(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamBundle::getNextStreamIndex(size_t& index) const
|
||||
{
|
||||
if (m_streams.empty()) { return false; }
|
||||
|
||||
// Find the stream with the earliest chunk, return the stream
|
||||
CTime earliestTime = CTime::max();
|
||||
bool foundSomething = false;
|
||||
|
||||
for (size_t i = 0; i < m_streams.size(); ++i)
|
||||
{
|
||||
const StreamPtr ptr = m_streams[i];
|
||||
|
||||
CTime startTime = CTime::min(), endTime = CTime::min();
|
||||
if (ptr && ptr->peek(startTime, endTime) && startTime < earliestTime)
|
||||
{
|
||||
earliestTime = startTime;
|
||||
index = int(i);
|
||||
foundSomething = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundSomething)
|
||||
{
|
||||
//log() << Kernel::LogLevel_Info << "All streams exhausted\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
StreamPtr StreamBundle::getNextStream(size_t& index)
|
||||
{
|
||||
index = -1;
|
||||
if (getNextStreamIndex(index)) { return m_streams[index]; }
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CTime StreamBundle::getMaxDuration() const
|
||||
{
|
||||
CTime maxDuration = CTime::min();
|
||||
for (size_t i = 0; i < m_streams.size(); ++i)
|
||||
{
|
||||
if (m_streams[i])
|
||||
{
|
||||
CTime streamDuration = m_streams[i]->getDuration();
|
||||
maxDuration = std::max<CTime>(maxDuration, streamDuration);
|
||||
}
|
||||
}
|
||||
return maxDuration;
|
||||
}
|
||||
|
||||
|
||||
bool StreamBundle::setStream(const size_t index, const std::shared_ptr<StreamBase>& ptr)
|
||||
{
|
||||
if (index >= m_streams.size()) { m_streams.resize(index + 1, nullptr); }
|
||||
m_streams[index] = ptr;
|
||||
setDirtyBit(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamBundle::swapStreams(const size_t idx1, const size_t idx2)
|
||||
{
|
||||
if (idx1 >= m_streams.size() || idx2 >= m_streams.size()) { return false; }
|
||||
|
||||
const auto it1 = m_streams.begin() + idx1;
|
||||
const auto it2 = m_streams.begin() + idx2;
|
||||
|
||||
std::iter_swap(it1, it2);
|
||||
|
||||
setDirtyBit(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamBundle::moveStream(const size_t srcIdx, const size_t dstIdx)
|
||||
{
|
||||
if (srcIdx >= getNumStreams() || dstIdx >= getNumStreams()) { return false; }
|
||||
if (srcIdx == dstIdx) { return true; }
|
||||
|
||||
const auto oldPtr = m_streams[srcIdx];
|
||||
m_streams.erase(m_streams.begin() + srcIdx);
|
||||
m_streams.insert(m_streams.begin() + dstIdx, oldPtr);
|
||||
|
||||
setDirtyBit(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+106
@@ -0,0 +1,106 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
// @author J.T. Lindgren
|
||||
//
|
||||
|
||||
// n.b. having this as a separate file so EBML/Demuxer dependencies do not get pulled into StreamBundle
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "StreamBundleImportExport.h"
|
||||
|
||||
#include "EBMLSourceFile.h"
|
||||
#include "Demuxer.h"
|
||||
#include "BoxAdapterGenericStreamWriter.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
StreamBundle* readStreamBundleFromFile(const Kernel::IKernelContext& ctx, const char* filename, const bool memorySaveMode)
|
||||
{
|
||||
StreamBundle* newTrack = new StreamBundle(ctx);
|
||||
|
||||
EBMLSourceFile origin(ctx);
|
||||
if (!origin.initialize(filename))
|
||||
{
|
||||
ctx.getLogManager() << Kernel::LogLevel_Error << "Unable to read from file " << filename << "\n";
|
||||
delete newTrack;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Construct a track from the origin stream
|
||||
Demuxer demux(ctx, origin, *newTrack);
|
||||
|
||||
if (memorySaveMode)
|
||||
{
|
||||
// Try to get the stream types but don't pull the buffers
|
||||
bool go = true;
|
||||
while (go)
|
||||
{
|
||||
if (!demux.step())
|
||||
{
|
||||
ctx.getLogManager() << Kernel::LogLevel_Trace << "Demuxer EOF\n";
|
||||
go = false;
|
||||
}
|
||||
|
||||
// n.b. here we use a heuristic that if at least a single chunk has been loaded,
|
||||
// then all the headers should have been demuxed before that. There is no guarantee
|
||||
// that this would always be the case, but it is very likely if the stream originates from
|
||||
// an .ov file created by OpenViBE
|
||||
for (size_t j = 0; j < newTrack->getNumStreams(); ++j)
|
||||
{
|
||||
if (newTrack->getStream(j)->getDuration().time() > 0)
|
||||
{
|
||||
go = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
origin.uninitialize();
|
||||
|
||||
// Release memory
|
||||
for (size_t i = 0; i < newTrack->getNumStreams(); ++i) { newTrack->getStream(i)->clear(); }
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just read the whole file to memory
|
||||
while (true)
|
||||
{
|
||||
if (!demux.step())
|
||||
{
|
||||
ctx.getLogManager() << Kernel::LogLevel_Trace << "Demuxer EOF\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
origin.uninitialize();
|
||||
newTrack->setDirtyBit(false);
|
||||
return newTrack;
|
||||
}
|
||||
|
||||
bool saveStreamBundleToFile(const Kernel::IKernelContext& ctx, StreamBundle* track, const char* filename)
|
||||
{
|
||||
BoxAdapterGenericStreamWriter writer(ctx, *track, filename);
|
||||
|
||||
bool retval = true;
|
||||
|
||||
retval &= writer.initialize();
|
||||
retval &= writer.spool(false);
|
||||
retval &= writer.uninitialize();
|
||||
|
||||
if (retval)
|
||||
{
|
||||
track->setSource(filename);
|
||||
retval &= true;
|
||||
track->setDirtyBit(false);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+322
@@ -0,0 +1,322 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
// @author J.T. Lindgren
|
||||
//
|
||||
// @todo add horizontal scaling support
|
||||
// @todo add event handlers
|
||||
// @todo add ruler, stimulations, channel names, a million of other things
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
#include <mensia/advanced-visualization.hpp>
|
||||
#include <m_GtkGL.hpp>
|
||||
|
||||
#include "StreamRendererBase.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
bool StreamRendererBase::initialize()
|
||||
{
|
||||
GtkBuilder* builder = gtk_builder_new();
|
||||
const CString filename = Directories::getDataDir() + "/applications/tracker/advanced-visualization.ui";
|
||||
GError* errorCode = nullptr;
|
||||
if (!gtk_builder_add_from_file(builder, filename, &errorCode))
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Problem loading [" << filename << "] : "
|
||||
<< (errorCode ? errorCode->code : 0) << " " << (errorCode ? errorCode->message : "") << "\n";
|
||||
g_object_unref(builder);
|
||||
return false;
|
||||
}
|
||||
|
||||
GtkWidget* window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
|
||||
m_main = GTK_WIDGET(gtk_builder_get_object(builder, "table"));
|
||||
// ::GtkWidget* toolbar = GTK_WIDGET(gtk_builder_get_object(pBuilder, "toolbar-window"));
|
||||
|
||||
gtk_widget_ref(m_main);
|
||||
gtk_container_remove(GTK_CONTAINER(window), m_main);
|
||||
// We keep one ref, the caller can unref after having assigned the widget
|
||||
|
||||
m_viewport = GTK_WIDGET(gtk_builder_get_object(builder, "viewport"));
|
||||
m_top = GTK_WIDGET(gtk_builder_get_object(builder, "label_top")); // caption
|
||||
m_left = GTK_WIDGET(gtk_builder_get_object(builder, "drawingarea_left"));
|
||||
m_right = GTK_WIDGET(gtk_builder_get_object(builder, "drawingarea_right"));
|
||||
m_bottom = GTK_WIDGET(gtk_builder_get_object(builder, "drawingarea_bottom"));
|
||||
m_cornerLeft = GTK_WIDGET(gtk_builder_get_object(builder, "label_corner_left"));
|
||||
m_cornerRight = GTK_WIDGET(gtk_builder_get_object(builder, "label_corner_right"));
|
||||
|
||||
// @note this or something similar is needed or otherwise the widgets will all be crammed to the
|
||||
// same fixed size aperture with no scrolling
|
||||
// @todo give users some scaling options
|
||||
// @fixme for some reason this causes a mess on the 'message' bar below in the UI, as if it didn't redraw properly
|
||||
gtk_widget_set_size_request(m_main, 640, 200);
|
||||
|
||||
m_color.r = 1;
|
||||
m_color.g = 1;
|
||||
m_color.b = 1;
|
||||
|
||||
g_object_unref(builder);
|
||||
builder = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererBase::uninitialize()
|
||||
{
|
||||
for (size_t i = 0; i < m_renderers.size(); ++i) { AdvancedVisualization::IRenderer::release(m_renderers[i]); }
|
||||
m_renderers.clear();
|
||||
|
||||
if (m_rendererCtx)
|
||||
{
|
||||
delete m_rendererCtx;
|
||||
m_rendererCtx = nullptr;
|
||||
}
|
||||
|
||||
if (m_subRendererCtx)
|
||||
{
|
||||
delete m_subRendererCtx;
|
||||
m_subRendererCtx = nullptr;
|
||||
}
|
||||
|
||||
if (m_ruler)
|
||||
{
|
||||
delete m_ruler;
|
||||
m_ruler = nullptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererBase::setTitle(const char* title)
|
||||
{
|
||||
if (title) { gtk_label_set_text(GTK_LABEL(m_top), title); }
|
||||
else { gtk_label_set_text(GTK_LABEL(m_top), ""); }
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererBase::setRulerVisibility(const bool isVisible)
|
||||
{
|
||||
m_isScaleVisible = isVisible;
|
||||
return updateRulerVisibility();
|
||||
}
|
||||
|
||||
bool StreamRendererBase::updateRulerVisibility()
|
||||
{
|
||||
/*
|
||||
if ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_pScaleVisible)) ? true : false) != m_isScaleVisible)
|
||||
{
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_pScaleVisible), m_isScaleVisible);
|
||||
}
|
||||
*/
|
||||
|
||||
void (*action)(GtkWidget*) = m_isScaleVisible ? gtk_widget_show : gtk_widget_hide;
|
||||
(*action)(this->m_top);
|
||||
(*action)(this->m_left);
|
||||
(*action)(this->m_right);
|
||||
(*action)(this->m_bottom);
|
||||
(*action)(this->m_cornerLeft);
|
||||
(*action)(this->m_cornerRight);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererBase::realize()
|
||||
{
|
||||
gtk_widget_realize(m_top);
|
||||
gtk_widget_realize(m_left);
|
||||
gtk_widget_realize(m_right);
|
||||
gtk_widget_realize(m_bottom);
|
||||
gtk_widget_realize(m_viewport);
|
||||
gtk_widget_realize(m_main);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool StreamRendererBase::redraw(const bool bImmediate /* = false */)
|
||||
{
|
||||
m_gtkGLWidget.redraw(bImmediate);
|
||||
m_gtkGLWidget.redrawLeft(bImmediate);
|
||||
m_gtkGLWidget.redrawRight(bImmediate);
|
||||
m_gtkGLWidget.redrawBottom(bImmediate);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererBase::reshape(const uint32_t width, const uint32_t height)
|
||||
{
|
||||
m_width = uint32_t(width);
|
||||
m_height = uint32_t(height);
|
||||
m_rendererCtx->setAspect(width * 1.0F / height);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererBase::draw()
|
||||
{
|
||||
StreamRendererBase::preDraw();
|
||||
|
||||
glPushAttrib(GL_ALL_ATTRIB_BITS);
|
||||
//::glClearColor(1.0,1.0,1.0,1.0);
|
||||
glColor4f(m_color.r, m_color.g, m_color.b, m_rendererCtx->getTranslucency());
|
||||
|
||||
if (m_rotate)
|
||||
{
|
||||
glScalef(1, -1, 1);
|
||||
glRotatef(-90, 0, 0, 1);
|
||||
}
|
||||
|
||||
m_renderers[0]->render(*m_rendererCtx);
|
||||
glPopAttrib();
|
||||
|
||||
StreamRendererBase::postDraw();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void StreamRendererBase::drawLeft() { if (m_ruler) { m_ruler->doRenderLeft(m_left); } }
|
||||
void StreamRendererBase::drawRight() { if (m_ruler) { m_ruler->doRenderRight(m_right); } }
|
||||
void StreamRendererBase::drawBottom() { if (m_ruler) { m_ruler->doRenderBottom(m_bottom); } }
|
||||
|
||||
bool StreamRendererBase::preDraw()
|
||||
{
|
||||
this->updateRulerVisibility();
|
||||
|
||||
// auto m_sColorGradient=CString("0:0,0,0; 100:100,100,100");
|
||||
|
||||
const char* gradient =
|
||||
"0:100, 100, 100; 12:50, 100, 100; 25:0, 50, 100; 38:0, 0, 50; 50:0, 0, 0; 62:50, 0, 0; 75:100, 50, 0; 88:100, 100, 50; 100:100, 100, 100";
|
||||
|
||||
if (!m_textureID) { m_textureID = m_gtkGLWidget.createTexture(gradient); }
|
||||
glBindTexture(GL_TEXTURE_1D, m_textureID);
|
||||
|
||||
m_rendererCtx->setAspect(m_viewport->allocation.width * 1.0F / m_viewport->allocation.height);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool StreamRendererBase::postDraw()
|
||||
{
|
||||
glPushAttrib(GL_ALL_ATTRIB_BITS);
|
||||
if (m_ruler) { m_ruler->doRender(); }
|
||||
glPopAttrib();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererBase::mouseButton(int /*x*/, int /*y*/, const int button, const int status)
|
||||
{
|
||||
m_buttons[button] = status;
|
||||
|
||||
/*
|
||||
if (button == 1 && status == 1)
|
||||
{
|
||||
m_isScaleVisible = !m_isScaleVisible;
|
||||
m_pRendererContext->setScaleVisibility(m_isScaleVisible);
|
||||
}
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererBase::mouseMotion(const int x, const int y)
|
||||
{
|
||||
if (!m_mouseInitialized)
|
||||
{
|
||||
m_mouseX = x;
|
||||
m_mouseY = y;
|
||||
m_mouseInitialized = true;
|
||||
}
|
||||
|
||||
if (m_buttons[3])
|
||||
{
|
||||
const float value = powf(0.99F, float(y - m_mouseY));
|
||||
// std::cout << "scale " << value << "\n";
|
||||
m_rendererCtx->scaleBy(value);
|
||||
redraw();
|
||||
}
|
||||
if (m_buttons[2])
|
||||
{
|
||||
const float value = powf(0.99F, float(y - m_mouseY));
|
||||
// std::cout << "zoom " << value << "\n";
|
||||
m_rendererCtx->zoomBy(value);
|
||||
}
|
||||
if (m_buttons[1])
|
||||
{
|
||||
// std::cout << "Rotate\n";
|
||||
m_rendererCtx->rotateByY(float(x - m_mouseX) * 0.1F);
|
||||
m_rendererCtx->rotateByX(float(y - m_mouseY) * 0.1F);
|
||||
}
|
||||
|
||||
m_mouseX = x;
|
||||
m_mouseY = y;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererBase::finalize()
|
||||
{
|
||||
for (size_t i = 0; i < m_renderers.size(); ++i)
|
||||
{
|
||||
m_renderers[i]->rebuild(*m_rendererCtx);
|
||||
m_renderers[i]->refresh(*m_rendererCtx);
|
||||
}
|
||||
redraw(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererBase::restoreSettings(const std::string& prefix)
|
||||
{
|
||||
if (!m_rendererCtx) { return false; }
|
||||
|
||||
// Lets see if we have a scale token
|
||||
const std::string token = std::string("${") + prefix + "_Scale}";
|
||||
const float newScale = float(m_kernelCtx.getConfigurationManager().expandAsFloat(token.c_str(), m_rendererCtx->getScale()));
|
||||
m_rendererCtx->setScale(newScale);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererBase::storeSettings(const std::string& prefix)
|
||||
{
|
||||
if (!m_rendererCtx) { return false; }
|
||||
|
||||
const std::string token = prefix + "_Scale";
|
||||
std::stringstream value;
|
||||
value << m_rendererCtx->getScale();
|
||||
m_kernelCtx.getConfigurationManager().addOrReplaceConfigurationToken(token.c_str(), value.str().c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
CString StreamRendererBase::renderAsText(const size_t indent) const
|
||||
{
|
||||
return (std::string(indent, ' ') + "Detail printing unimplemented for stream type or placeholder renderer in use\n").c_str();
|
||||
}
|
||||
|
||||
void add_column(GtkTreeView* treeView, const char* name, const uint32_t id, const uint32_t minWidth)
|
||||
{
|
||||
GtkTreeViewColumn* column = gtk_tree_view_column_new();
|
||||
GtkCellRenderer* cell = gtk_cell_renderer_text_new();
|
||||
gtk_tree_view_column_set_title(column, name);
|
||||
gtk_tree_view_column_pack_start(column, cell, TRUE);
|
||||
gtk_tree_view_column_set_attributes(column, cell, "text", id, nullptr);
|
||||
gtk_tree_view_column_set_sort_column_id(column, id);
|
||||
gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
|
||||
gtk_tree_view_column_set_expand(column, TRUE);
|
||||
gtk_tree_view_column_set_resizable(column, TRUE);
|
||||
gtk_tree_view_column_set_min_width(column, minWidth);
|
||||
gtk_tree_view_append_column(treeView, column);
|
||||
gtk_tree_view_column_set_sort_indicator(column, TRUE);
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
// @author J.T. Lindgren
|
||||
//
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
#include "StreamRendererChannelLocalization.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
CString StreamRendererChannelLocalization::renderAsText(const size_t indent) const
|
||||
{
|
||||
const TypeChannelLocalization::Header& hdr = m_Stream->getHeader();
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
ss << std::string(indent, ' ') << "Dynamic: " << (hdr.m_Dynamic ? "True" : "False") << std::endl;
|
||||
|
||||
for (size_t i = 0; i < m_Stream->getChunkCount(); ++i)
|
||||
{
|
||||
const TypeChannelLocalization::Buffer* buf = m_Stream->getChunk(i);
|
||||
|
||||
ss << std::string(indent, ' ') << "Configuration at time " << buf->m_StartTime.toSeconds() << "s:" << std::endl;
|
||||
|
||||
const double* ptr = buf->m_buffer.getBuffer();
|
||||
|
||||
for (uint32_t chn = 0; chn < buf->m_buffer.getDimensionSize(0); ++chn)
|
||||
{
|
||||
ss << std::string(indent, ' ') << " Channel " << chn << " (" << hdr.m_Header.getDimensionLabel(0, chn) << ") " << "x=" << ptr[chn * 3 + 0] << " "
|
||||
<< "y=" << ptr[chn * 3 + 1] << " " << "z=" << ptr[chn * 3 + 2] << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return ss.str().c_str();
|
||||
}
|
||||
|
||||
bool StreamRendererChannelLocalization::showChunkList() { return StreamRendererLabel::showChunkList("Channel localization stream details"); }
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Masterarbeit/openvibe/extras-master/applications/platform/tracker/src/StreamRendererChannelUnits.cpp
Executable
+49
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
// @author J.T. Lindgren
|
||||
//
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <system/ovCTime.h>
|
||||
#include "StreamRendererChannelUnits.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
CString StreamRendererChannelUnits::renderAsText(const size_t indent) const
|
||||
{
|
||||
const TypeChannelUnits::Header& hdr = m_stream->getHeader();
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
ss << std::string(indent, ' ') << "Dynamic: "
|
||||
<< (hdr.m_Dynamic ? "True" : "False") << std::endl;
|
||||
|
||||
for (size_t i = 0; i < m_stream->getChunkCount(); ++i)
|
||||
{
|
||||
const TypeChannelUnits::Buffer* buf = m_stream->getChunk(i);
|
||||
|
||||
ss << std::string(indent, ' ') << "Configuration at time " << buf->m_StartTime.toSeconds() << "s:" << std::endl;
|
||||
|
||||
const double* ptr = buf->m_buffer.getBuffer();
|
||||
|
||||
for (uint32_t chn = 0; chn < buf->m_buffer.getDimensionSize(0); ++chn)
|
||||
{
|
||||
const CString unit = m_kernelCtx.getTypeManager().getEnumerationEntryNameFromValue(OV_TypeId_MeasurementUnit, uint64_t(ptr[chn * 2 + 0]));
|
||||
const CString factor = m_kernelCtx.getTypeManager().getEnumerationEntryNameFromValue(OV_TypeId_Factor, uint64_t(ptr[chn * 2 + 1]));
|
||||
|
||||
ss << std::string(indent, ' ') << " Channel " << chn << " (" << hdr.m_Header.getDimensionLabel(0, chn) << ") " << "Unit: " << unit << ", Factor: "
|
||||
<< factor << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return ss.str().c_str();
|
||||
}
|
||||
|
||||
bool StreamRendererChannelUnits::showChunkList() { return StreamRendererLabel::showChunkList("Channel units stream details"); }
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
// @author J.T. Lindgren
|
||||
//
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
#include "StreamRendererExperimentInfo.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
CString StreamRendererExperimentInfo::renderAsText(const size_t indent) const
|
||||
{
|
||||
const TypeExperimentInfo::Header& hdr = m_stream->getHeader();
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
ss << std::string(indent, ' ') << "Experiment id: " << hdr.m_ExperimentID << std::endl;
|
||||
ss << std::string(indent, ' ') << "Experiment date: " << hdr.m_ExperimentDate << std::endl;
|
||||
|
||||
ss << std::string(indent, ' ') << "Subject id: " << hdr.m_SubjectID << std::endl;
|
||||
ss << std::string(indent, ' ') << "Subject name: " << hdr.m_SubjectName << std::endl;
|
||||
ss << std::string(indent, ' ') << "Subject age: " << hdr.m_SubjectAge << std::endl;
|
||||
ss << std::string(indent, ' ') << "Subject gender: " << hdr.m_SubjectGender << std::endl;
|
||||
|
||||
ss << std::string(indent, ' ') << "Laboratory id: " << hdr.m_LaboratoryID << std::endl;
|
||||
ss << std::string(indent, ' ') << "Laboratory name: " << hdr.m_LaboratoryName << std::endl;
|
||||
ss << std::string(indent, ' ') << "Technician id: " << hdr.m_TechnicianID << std::endl;
|
||||
ss << std::string(indent, ' ') << "Technician name: " << hdr.m_TechnicianName << std::endl;
|
||||
|
||||
// ss << string(indent, ' ') << "Channels: " << m_Header.m_header.getDimensionSize(0) << std::endl;
|
||||
// ss << string(indent, ' ') << "Samples per chunk: " << m_Header.m_header.getDimensionSize(1) << std::endl;
|
||||
return ss.str().c_str();
|
||||
}
|
||||
|
||||
bool StreamRendererExperimentInfo::showChunkList() { return StreamRendererLabel::showChunkList("Experiment information stream details"); }
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+82
@@ -0,0 +1,82 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
// @author J.T. Lindgren
|
||||
//
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
#include "StreamRendererLabel.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
bool StreamRendererLabel::initialize()
|
||||
{
|
||||
m_main = gtk_frame_new("");
|
||||
|
||||
m_label = gtk_label_new("");
|
||||
gtk_label_set_justify(GTK_LABEL(m_label), GTK_JUSTIFY_LEFT);
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(m_main), m_label);
|
||||
|
||||
gtk_widget_ref(m_main);
|
||||
|
||||
// @fixme hardcoded numbers likely not such a great idea as different font sizes etc
|
||||
gtk_widget_set_size_request(m_main, 640, 40);
|
||||
|
||||
gtk_widget_show(m_main);
|
||||
gtk_widget_show(m_label);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererLabel::setTitle(const char* title)
|
||||
{
|
||||
gtk_label_set(GTK_LABEL(m_label), title);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Instead of showing chunk list, this prints the stream structure information
|
||||
// in a window. How it works is that a derived class can implement a text render
|
||||
// routine which this function then calls. This way each derived class
|
||||
// doesn't need to copy-paste the identical window code.
|
||||
bool StreamRendererLabel::showChunkList(const char* title)
|
||||
{
|
||||
// @fixme Not very pretty but better than nothing; here we have the benefit
|
||||
// that since we make a new builder here, reusing the workspace information
|
||||
// window does not mess up the real one
|
||||
|
||||
GtkBuilder* builder = gtk_builder_new();
|
||||
const CString filename = Directories::getDataDir() + "/applications/tracker/tracker.ui";
|
||||
if (!gtk_builder_add_from_file(builder, filename, nullptr))
|
||||
{
|
||||
std::cout << "Problem loading [" << filename << "]\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
GtkWidget* window = GTK_WIDGET(gtk_builder_get_object(builder, "tracker-workspace_information"));
|
||||
GtkTextBuffer* buffer = GTK_TEXT_BUFFER(gtk_builder_get_object(builder, "tracker-textbuffer-workspace_information"));
|
||||
|
||||
gtk_window_set_title(GTK_WINDOW(window), title);
|
||||
|
||||
// Hide instead of destroy on closing the window
|
||||
g_signal_connect(window, "delete_event", G_CALLBACK(gtk_widget_hide_on_delete), nullptr);
|
||||
|
||||
const CString details = renderAsText(0);
|
||||
|
||||
gtk_text_buffer_set_text(buffer, details.toASCIIString(), -1);
|
||||
gtk_widget_show_all(window);
|
||||
gtk_window_present(GTK_WINDOW(window));
|
||||
g_object_unref(builder);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+235
@@ -0,0 +1,235 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
// @author J.T. Lindgren
|
||||
//
|
||||
// @todo add horizontal scaling support
|
||||
// @todo add event handlers
|
||||
// @todo add ruler, stimulations, channel names, a million of other things
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <mensia/advanced-visualization.hpp>
|
||||
#include <m_GtkGL.hpp>
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
#include "StreamRendererMatrix.h"
|
||||
|
||||
#include "ruler/mTRulerAutoType.hpp"
|
||||
#include "ruler/mTRulerPair.hpp"
|
||||
#include "ruler/mTRulerConditionalPair.hpp"
|
||||
#include "ruler/mCRulerConditionIsTimeLocked.hpp"
|
||||
|
||||
#include "ruler/mCRulerProgressV.hpp"
|
||||
|
||||
#include "ruler/mCRulerBottomCount.hpp"
|
||||
#include "ruler/mCRulerBottomTime.hpp"
|
||||
|
||||
#include "ruler/mCRulerLeftChannelNames.hpp"
|
||||
|
||||
#include "ruler/mCRulerRightCount.hpp"
|
||||
#include "ruler/mCRulerRightScale.hpp"
|
||||
#include "ruler/mCRulerRightLabels.hpp"
|
||||
#include "ruler/mCRulerRightFrequency.hpp"
|
||||
|
||||
#include "ruler/mCRulerRightTexture.hpp"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
bool StreamRendererMatrix::initialize()
|
||||
{
|
||||
const TypeMatrix::Buffer* firstChunk = m_stream->getChunk(0);
|
||||
if (firstChunk)
|
||||
{
|
||||
m_chunkDuration = firstChunk->m_EndTime - firstChunk->m_StartTime;
|
||||
|
||||
if (firstChunk->m_buffer.getDimensionCount() == 1)
|
||||
{
|
||||
// Degenerate case like a feature vector
|
||||
m_nRows = firstChunk->m_buffer.getDimensionSize(0);
|
||||
m_nCols = 0;
|
||||
}
|
||||
else if (firstChunk->m_buffer.getDimensionCount() == 2)
|
||||
{
|
||||
m_nRows = firstChunk->m_buffer.getDimensionSize(0);
|
||||
m_nCols = firstChunk->m_buffer.getDimensionSize(1);
|
||||
}
|
||||
else if (firstChunk->m_buffer.getDimensionCount() > 2)
|
||||
{
|
||||
std::cout << "Warning: The matrix renderer does not work correctly if dims>2 (for tensors)\n";
|
||||
m_nRows = firstChunk->m_buffer.getDimensionSize(0);
|
||||
m_nCols = firstChunk->m_buffer.getDimensionSize(1);
|
||||
}
|
||||
else { log() << Kernel::LogLevel_Error << "Error: Dimension count " << firstChunk->m_buffer.getDimensionCount() << " not supported\n"; }
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Stream is empty\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
m_rendererCtx = new AdvancedVisualization::CRendererContext();
|
||||
m_rendererCtx->clear();
|
||||
m_rendererCtx->setTimeScale(1);
|
||||
m_rendererCtx->setScaleVisibility(m_isScaleVisible);
|
||||
m_rendererCtx->setCheckBoardVisibility(true);
|
||||
m_rendererCtx->setTimeLocked(true);
|
||||
m_rendererCtx->setPositiveOnly(false);
|
||||
m_rendererCtx->setDataType(AdvancedVisualization::CRendererContext::EDataType::Matrix);
|
||||
m_rendererCtx->setSampleDuration(m_chunkDuration.time());
|
||||
m_rendererCtx->setParentRendererContext(&AdvancedVisualization::getContext());
|
||||
|
||||
m_rendererCtx->setAxisDisplay(true);
|
||||
|
||||
auto& hdr = m_stream->getHeader();
|
||||
|
||||
for (uint32_t j = 0; j < m_nRows; ++j) { m_rendererCtx->addChannel(std::string(hdr.m_Header.getDimensionLabel(0, j))); }
|
||||
|
||||
m_swap.resize(m_nRows);
|
||||
|
||||
m_renderers.push_back(AdvancedVisualization::IRenderer::create(AdvancedVisualization::ERendererType::Bitmap, false));
|
||||
|
||||
m_ruler = new AdvancedVisualization::TRulerPair<AdvancedVisualization::CRulerProgressV, AdvancedVisualization::TRulerPair<
|
||||
AdvancedVisualization::TRulerAutoType<
|
||||
AdvancedVisualization::IRuler, AdvancedVisualization::TRulerConditionalPair<
|
||||
AdvancedVisualization::CRulerBottomTime, AdvancedVisualization::CRulerBottomCount,
|
||||
AdvancedVisualization::CRulerConditionIsTimeLocked>, AdvancedVisualization::IRuler>,
|
||||
AdvancedVisualization::TRulerPair<
|
||||
AdvancedVisualization::CRulerLeftChannelNames, AdvancedVisualization::CRulerRightTexture>>>;
|
||||
|
||||
// m_pRuler = new TRulerPair < TRulerConditionalPair < CRulerBottomTime, CRulerBottomCount, CRulerConditionIsTimeLocked >, TRulerPair < TRulerAutoType < IRuler, IRuler, CRulerRightFrequency >, TRulerPair < CRulerLeftChannelNames, CRulerProgressV > > >;
|
||||
m_ruler->setRendererContext(m_rendererCtx);
|
||||
m_ruler->setRenderer(m_renderers[0]);
|
||||
|
||||
if (!StreamRendererBase::initialize()) { return false; }
|
||||
|
||||
m_gtkGLWidget.initialize(*this, m_viewport, m_left, m_right, m_bottom);
|
||||
m_gtkGLWidget.setPointSmoothingActive(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool StreamRendererMatrix::reset(const CTime startTime, const CTime endTime)
|
||||
{
|
||||
m_startTime = startTime;
|
||||
m_endTime = endTime;
|
||||
|
||||
const uint32_t numBuffers = ((m_endTime - m_startTime).ceil().time() / m_chunkDuration.time());
|
||||
|
||||
m_rendererCtx->setElementCount(numBuffers);
|
||||
|
||||
m_renderers[0]->clear(0);
|
||||
m_renderers[0]->setSampleCount(numBuffers); // $$$
|
||||
m_renderers[0]->setChannelCount(m_nRows);
|
||||
|
||||
// @FIXME The offset is needed to have correct numbers on the ruler; remove ifdef once the feature is in
|
||||
#ifdef RENDERER_SUPPORTS_OFFSET
|
||||
m_renderers[0]->setTimeOffset(m_startTime.time());
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool StreamRendererMatrix::push(const TypeMatrix::Buffer& chunk, bool /*zeroInput*/)
|
||||
{
|
||||
#if 0
|
||||
static uint32_t pushed = 0;
|
||||
std::cout << "Push spec chk " << pushed << " " << chunk.m_buffer.getDimensionSize(0)
|
||||
<< " " << chunk.m_buffer.getDimensionSize(1) << " "
|
||||
<< CTime(chunk.m_startTime).toSeconds() << ","
|
||||
<< CTime(chunk.m_endTime).toSeconds()
|
||||
<< "\n";
|
||||
std::cout << pushed << " first bytes "
|
||||
<< chunk.m_buffer.getBuffer()[0]
|
||||
<< chunk.m_buffer.getBuffer()[1]
|
||||
<< chunk.m_buffer.getBuffer()[2]
|
||||
<< chunk.m_buffer.getBuffer()[3]
|
||||
<< "\n";
|
||||
pushed++;
|
||||
#endif
|
||||
|
||||
|
||||
m_rendererCtx->setSpectrumFrequencyRange(uint32_t((uint64_t(m_nRows) << 32) / m_chunkDuration.time()));
|
||||
|
||||
// Handle the degenerate case NumCols=0
|
||||
const size_t actualCols = std::max<size_t>(m_nCols, 1);
|
||||
|
||||
// Feed renderer with actual samples
|
||||
for (uint32_t j = 0; j < actualCols; ++j)
|
||||
{
|
||||
for (uint32_t k = 0; k < m_nRows; ++k) { m_swap[k] = float(chunk.m_buffer.getBuffer()[k * actualCols + j]); }
|
||||
m_renderers[0]->feed(&m_swap[0]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool StreamRendererMatrix::draw()
|
||||
{
|
||||
StreamRendererMatrix::preDraw();
|
||||
|
||||
glPushAttrib(GL_ALL_ATTRIB_BITS);
|
||||
glColor4f(m_color.r, m_color.g, m_color.b, m_rendererCtx->getTranslucency());
|
||||
m_renderers[0]->render(*m_rendererCtx);
|
||||
glPopAttrib();
|
||||
|
||||
StreamRendererMatrix::postDraw();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererMatrix::preDraw()
|
||||
{
|
||||
this->updateRulerVisibility();
|
||||
|
||||
// auto m_sColorGradient=CString("0:0,0,0; 100:100,100,100");
|
||||
const std::string gradient = "0:0, 0, 50; 25:0, 100, 100; 50:0, 50, 0; 75:100, 100, 0; 100:75, 0, 0";
|
||||
// auto m_sColorGradient = CString("0:100, 100, 100; 12:50, 100, 100; 25:0, 50, 100; 38:0, 0, 50; 50:0, 0, 0; 62:50, 0, 0; 75:100, 50, 0; 88:100, 100, 50; 100:100, 100, 100");
|
||||
|
||||
if (!m_textureID) { m_textureID = m_gtkGLWidget.createTexture(gradient); }
|
||||
glBindTexture(GL_TEXTURE_1D, m_textureID);
|
||||
|
||||
m_rendererCtx->setAspect(m_viewport->allocation.width * 1.0F / m_viewport->allocation.height);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererMatrix::finalize()
|
||||
{
|
||||
m_renderers[0]->rebuild(*m_rendererCtx);
|
||||
m_renderers[0]->refresh(*m_rendererCtx);
|
||||
|
||||
redraw(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CString StreamRendererMatrix::renderAsText(const size_t indent) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::string(indent, ' ') << "Rows: " << m_nRows << std::endl;
|
||||
ss << std::string(indent, ' ') << "Cols: " << m_nCols << std::endl;
|
||||
|
||||
// ss << std::string(indent, ' ') << "Channels: " << m_Header.m_header.getDimensionSize(0) << std::endl;
|
||||
// ss << std::string(indent, ' ') << "Samples per chunk: " << m_Header.m_header.getDimensionSize(1) << std::endl;
|
||||
return ss.str().c_str();
|
||||
}
|
||||
|
||||
bool StreamRendererMatrix::mouseButton(const int x, const int y, const int button, const int status)
|
||||
{
|
||||
//if (button == 3 && status == 1) { showChunkList(); }
|
||||
return StreamRendererBase::mouseButton(x, y, button, status);
|
||||
}
|
||||
|
||||
bool StreamRendererMatrix::showChunkList() { return showMatrixList<TypeMatrix>(m_stream, &m_streamListWindow, "List of chunks for Matrix stream"); }
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+153
@@ -0,0 +1,153 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
// @author J.T. Lindgren
|
||||
//
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <mensia/advanced-visualization.hpp>
|
||||
#include <mTGtkGLWidget.hpp>
|
||||
#include <m_GtkGL.hpp>
|
||||
|
||||
#include "StreamRendererSignal.h"
|
||||
|
||||
#include "ruler/mTRulerAutoType.hpp"
|
||||
#include "ruler/mTRulerPair.hpp"
|
||||
#include "ruler/mTRulerConditionalPair.hpp"
|
||||
#include "ruler/mCRulerConditionIsTimeLocked.hpp"
|
||||
|
||||
#include "ruler/mCRulerProgressV.hpp"
|
||||
|
||||
#include "ruler/mCRulerBottomCount.hpp"
|
||||
#include "ruler/mCRulerBottomTime.hpp"
|
||||
|
||||
#include "ruler/mCRulerLeftChannelNames.hpp"
|
||||
|
||||
#include "ruler/mCRulerRightCount.hpp"
|
||||
#include "ruler/mCRulerRightScale.hpp"
|
||||
#include "ruler/mCRulerRightLabels.hpp"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
bool StreamRendererSignal::initialize()
|
||||
{
|
||||
// TRendererStimulation < false, CRendererLine >:new CRendererLine
|
||||
AdvancedVisualization::IRenderer* pRenderer = AdvancedVisualization::IRenderer::create(AdvancedVisualization::ERendererType::Line, true);
|
||||
if (pRenderer == nullptr) { return false; }
|
||||
|
||||
const TypeSignal::Buffer* firstChunk = m_stream->getChunk(0);
|
||||
if (!firstChunk) { return false; }
|
||||
|
||||
m_nChannel = firstChunk->m_buffer.getDimensionSize(0);
|
||||
m_samplesPerChunk = firstChunk->m_buffer.getDimensionSize(1);
|
||||
m_chunkDuration = firstChunk->m_EndTime - firstChunk->m_StartTime;
|
||||
|
||||
// Creates renderer context
|
||||
m_rendererCtx = new AdvancedVisualization::CRendererContext();
|
||||
m_rendererCtx->clear();
|
||||
m_rendererCtx->setScale(1.0);
|
||||
m_rendererCtx->setTimeScale(1);
|
||||
m_rendererCtx->setCheckBoardVisibility(true);
|
||||
m_rendererCtx->setScaleVisibility(m_isScaleVisible);
|
||||
m_rendererCtx->setDataType(AdvancedVisualization::CRendererContext::EDataType::Signal);
|
||||
const CTime sampleDuration = CTime(m_chunkDuration.time() / m_samplesPerChunk);
|
||||
m_rendererCtx->setSampleDuration(sampleDuration.time());
|
||||
|
||||
const TypeSignal::Header& hdr = m_stream->getHeader();
|
||||
for (uint32_t i = 0; i < m_nChannel; ++i)
|
||||
{
|
||||
const char* label = hdr.m_Header.getDimensionLabel(0, i);
|
||||
m_rendererCtx->addChannel(std::string(label));
|
||||
}
|
||||
|
||||
m_ruler = new AdvancedVisualization::TRulerPair<AdvancedVisualization::CRulerProgressV, AdvancedVisualization::TRulerPair<
|
||||
AdvancedVisualization::TRulerAutoType<
|
||||
AdvancedVisualization::IRuler, AdvancedVisualization::TRulerConditionalPair<
|
||||
AdvancedVisualization::CRulerBottomTime, AdvancedVisualization::CRulerBottomCount,
|
||||
AdvancedVisualization::CRulerConditionIsTimeLocked>, AdvancedVisualization::IRuler>,
|
||||
AdvancedVisualization::TRulerPair<
|
||||
AdvancedVisualization::CRulerLeftChannelNames, AdvancedVisualization::CRulerRightScale>>>;
|
||||
|
||||
m_ruler->setRendererContext(m_rendererCtx);
|
||||
m_ruler->setRenderer(pRenderer);
|
||||
|
||||
if (!StreamRendererBase::initialize()) { return false; }
|
||||
|
||||
m_gtkGLWidget.initialize(*this, m_viewport, m_left, m_right, m_bottom);
|
||||
m_gtkGLWidget.setPointSmoothingActive(false);
|
||||
|
||||
m_renderers.push_back(pRenderer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererSignal::reset(const CTime startTime, const CTime endTime)
|
||||
{
|
||||
m_startTime = startTime;
|
||||
m_endTime = endTime;
|
||||
|
||||
// std::cout << "Overridden signal renderer reset\n";
|
||||
|
||||
m_renderers[0]->clear(0);
|
||||
|
||||
// std::cout << "Start time is " << CTime(m_StartTime).toSeconds()
|
||||
// << " end is " << CTime(m_EndTime).toSeconds() << "\n";
|
||||
|
||||
const uint64_t chunkCount = (m_endTime - m_startTime).ceil().time() / m_chunkDuration.time();
|
||||
const uint32_t numSamples = uint32_t(m_samplesPerChunk * chunkCount);
|
||||
|
||||
m_renderers[0]->setSampleCount(numSamples);
|
||||
m_renderers[0]->setChannelCount(m_nChannel);
|
||||
|
||||
// @FIXME The offset is needed to have correct numbers on the ruler; remove ifdef once the feature is in
|
||||
#ifdef RENDERER_SUPPORTS_OFFSET
|
||||
m_renderers[0]->setTimeOffset(m_startTime.time());
|
||||
#endif
|
||||
|
||||
// m_renderers[0]->setHistoryDrawIndex(samplesBeforeStart);
|
||||
m_renderers[0]->rebuild(*m_rendererCtx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererSignal::push(const TypeSignal::Buffer& chunk, const bool zeroInput /* = false */)
|
||||
{
|
||||
std::vector<float> tmp;
|
||||
if (!zeroInput)
|
||||
{
|
||||
tmp.resize(chunk.m_buffer.getBufferElementCount());
|
||||
for (size_t i = 0; i < chunk.m_buffer.getBufferElementCount(); ++i) { tmp[i] = float(chunk.m_buffer.getBuffer()[i]); }
|
||||
}
|
||||
else { tmp.resize(chunk.m_buffer.getBufferElementCount(), 0); }
|
||||
|
||||
m_renderers[0]->feed(&tmp[0], chunk.m_buffer.getDimensionSize(1));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CString StreamRendererSignal::renderAsText(const size_t indent) const
|
||||
{
|
||||
auto& hdr = m_stream->getHeader();
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::string(indent, ' ') << "Sampling rate: " << hdr.m_Sampling << "hz" << std::endl;
|
||||
ss << std::string(indent, ' ') << "Channels: " << hdr.m_Header.getDimensionSize(0) << std::endl;
|
||||
ss << std::string(indent, ' ') << "Samples per chunk: " << hdr.m_Header.getDimensionSize(1) << std::endl;
|
||||
|
||||
return ss.str().c_str();
|
||||
}
|
||||
|
||||
bool StreamRendererSignal::mouseButton(const int x, const int y, const int button, const int status)
|
||||
{
|
||||
//if (button == 3 && status == 1) { showChunkList(); }
|
||||
return StreamRendererBase::mouseButton(x, y, button, status);
|
||||
}
|
||||
|
||||
bool StreamRendererSignal::showChunkList() { return showMatrixList<TypeSignal>(m_stream, &m_streamListWindow, "List of chunks for Signal stream"); }
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+270
@@ -0,0 +1,270 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
// @author J.T. Lindgren
|
||||
//
|
||||
// @todo add horizontal scaling support
|
||||
// @todo add event handlers
|
||||
// @todo add ruler, stimulations, channel names, a million of other things
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <mensia/advanced-visualization.hpp>
|
||||
#include <m_GtkGL.hpp>
|
||||
|
||||
#include <system/ovCTime.h>
|
||||
|
||||
#include "StreamRendererSpectrum.h"
|
||||
|
||||
#include "ruler/mTRulerAutoType.hpp"
|
||||
#include "ruler/mTRulerPair.hpp"
|
||||
#include "ruler/mTRulerConditionalPair.hpp"
|
||||
#include "ruler/mCRulerConditionIsTimeLocked.hpp"
|
||||
|
||||
#include "ruler/mCRulerProgressV.hpp"
|
||||
|
||||
#include "ruler/mCRulerBottomCount.hpp"
|
||||
#include "ruler/mCRulerBottomTime.hpp"
|
||||
|
||||
#include "ruler/mCRulerLeftChannelNames.hpp"
|
||||
|
||||
#include "ruler/mCRulerRightCount.hpp"
|
||||
#include "ruler/mCRulerRightScale.hpp"
|
||||
#include "ruler/mCRulerRightLabels.hpp"
|
||||
#include "ruler/mCRulerRightFrequency.hpp"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
bool StreamRendererSpectrum::initialize()
|
||||
{
|
||||
const TypeSpectrum::Buffer* firstChunk = m_stream->getChunk(0);
|
||||
if (!firstChunk) { return false; }
|
||||
|
||||
m_nChannel = firstChunk->m_buffer.getDimensionSize(0);
|
||||
m_spectrumElements = firstChunk->m_buffer.getDimensionSize(1);
|
||||
m_chunkDuration = firstChunk->m_EndTime - firstChunk->m_StartTime;
|
||||
|
||||
m_rendererCtx = new AdvancedVisualization::CRendererContext();
|
||||
m_rendererCtx->clear();
|
||||
m_rendererCtx->setTimeScale(1);
|
||||
m_rendererCtx->setScaleVisibility(m_isScaleVisible);
|
||||
m_rendererCtx->setCheckBoardVisibility(true);
|
||||
m_rendererCtx->setTimeLocked(true);
|
||||
m_rendererCtx->setDataType(AdvancedVisualization::CRendererContext::EDataType::Spectrum);
|
||||
m_rendererCtx->setSampleDuration(m_chunkDuration.time());
|
||||
|
||||
const TypeSpectrum::Header& hdr = m_stream->getHeader();
|
||||
for (uint32_t j = 0; j < m_nChannel; ++j)
|
||||
{
|
||||
const char* name = hdr.m_Header.getDimensionLabel(0, j);
|
||||
m_rendererCtx->addChannel(std::string(name));
|
||||
}
|
||||
|
||||
m_subRendererCtx = new AdvancedVisualization::CRendererContext();
|
||||
m_subRendererCtx->clear();
|
||||
m_subRendererCtx->setParentRendererContext(m_rendererCtx);
|
||||
m_subRendererCtx->setTimeLocked(true);
|
||||
m_subRendererCtx->setStackCount(m_nChannel);
|
||||
m_subRendererCtx->setDataType(AdvancedVisualization::CRendererContext::EDataType::Spectrum);
|
||||
m_subRendererCtx->setSampleDuration(m_chunkDuration.time());
|
||||
|
||||
for (uint32_t j = 0; j < m_spectrumElements; ++j)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << j;
|
||||
m_subRendererCtx->addChannel(ss.str());
|
||||
}
|
||||
|
||||
m_swaps.resize(m_spectrumElements);
|
||||
|
||||
for (size_t i = 0; i < m_renderers.size(); ++i) { AdvancedVisualization::IRenderer::release(m_renderers[i]); }
|
||||
m_renderers.clear();
|
||||
m_renderers.resize(m_nChannel);
|
||||
|
||||
for (uint32_t j = 0; j < m_nChannel; ++j)
|
||||
{
|
||||
m_renderers[j] = AdvancedVisualization::IRenderer::create(AdvancedVisualization::ERendererType::Bitmap, false);
|
||||
m_renderers[j]->setChannelCount(m_spectrumElements);
|
||||
}
|
||||
|
||||
m_ruler = new AdvancedVisualization::TRulerPair<AdvancedVisualization::TRulerConditionalPair<
|
||||
AdvancedVisualization::CRulerBottomTime, AdvancedVisualization::CRulerBottomCount,
|
||||
AdvancedVisualization::CRulerConditionIsTimeLocked>, AdvancedVisualization::TRulerPair<
|
||||
AdvancedVisualization::TRulerAutoType<
|
||||
AdvancedVisualization::IRuler, AdvancedVisualization::IRuler,
|
||||
AdvancedVisualization::CRulerRightFrequency>, AdvancedVisualization::TRulerPair<
|
||||
AdvancedVisualization::CRulerLeftChannelNames, AdvancedVisualization::CRulerProgressV>>>;
|
||||
m_ruler->setRendererContext(m_rendererCtx);
|
||||
m_ruler->setRenderer(m_renderers[0]);
|
||||
|
||||
if (!StreamRendererBase::initialize()) { return false; }
|
||||
|
||||
m_gtkGLWidget.initialize(*this, m_viewport, m_left, m_right, m_bottom);
|
||||
m_gtkGLWidget.setPointSmoothingActive(false);
|
||||
|
||||
m_rotate = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool StreamRendererSpectrum::reset(const CTime startTime, const CTime endTime)
|
||||
{
|
||||
m_startTime = startTime;
|
||||
m_endTime = endTime;
|
||||
|
||||
// Each spectrum buffer has one spectrum per channel, so numBuffers is just:
|
||||
// ( @todo: is this really needed in addition to setSampleCount? )
|
||||
const size_t numBuffers = (m_endTime - m_startTime).ceil().time() / m_chunkDuration.time();
|
||||
m_rendererCtx->setElementCount(numBuffers);
|
||||
|
||||
for (size_t j = 0; j < m_nChannel; ++j)
|
||||
{
|
||||
m_renderers[j]->clear(0);
|
||||
m_renderers[j]->setSampleCount(numBuffers); // $$$
|
||||
}
|
||||
|
||||
// @FIXME The offset is needed to have correct numbers on the ruler; remove ifdef once the feature is in
|
||||
#ifdef RENDERER_SUPPORTS_OFFSET
|
||||
m_renderers[0]->setTimeOffset(m_startTime.time());
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Spectrum chunks are organized as [channel x freqs], so to push
|
||||
// with freqs on the y axis for each subrenderer, we transpose
|
||||
bool StreamRendererSpectrum::push(const TypeSpectrum::Buffer& chunk, const bool zeroInput /* = false */)
|
||||
{
|
||||
#if 0
|
||||
std::cout << "Push spec chk " << m_Pushed << " " << chunk.m_buffer.getDimensionSize(0) << " " << chunk.m_buffer.getDimensionSize(1) << " "
|
||||
<< CTime(chunk.m_startTime).toSeconds() << "," << CTime(chunk.m_endTime).toSeconds() << "\n";
|
||||
std::cout << m_Pushed << " first bytes " << chunk.m_buffer.getBuffer()[0] << chunk.m_buffer.getBuffer()[1]
|
||||
<< chunk.m_buffer.getBuffer()[2] << chunk.m_buffer.getBuffer()[3] << "\n";
|
||||
m_Pushed++;
|
||||
#endif
|
||||
|
||||
const size_t numFreq = chunk.m_buffer.getDimensionSize(1);
|
||||
|
||||
m_rendererCtx->setSpectrumFrequencyRange(uint32_t((uint64_t(numFreq) << 32) / m_chunkDuration.time()));
|
||||
|
||||
for (uint32_t j = 0; j < m_renderers.size(); ++j)
|
||||
{
|
||||
if (!zeroInput)
|
||||
{
|
||||
// Feed renderer with actual samples
|
||||
for (uint32_t k = 0; k < numFreq; ++k) { m_swaps[numFreq - k - 1] = float(chunk.m_buffer.getBuffer()[j * numFreq + k]); }
|
||||
}
|
||||
else { std::fill(m_swaps.begin(), m_swaps.end(), 0.0F); }
|
||||
m_renderers[j]->feed(&m_swaps[0]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool StreamRendererSpectrum::draw()
|
||||
{
|
||||
StreamRendererSpectrum::preDraw();
|
||||
|
||||
if (m_rendererCtx->getSelectedCount() != 0)
|
||||
{
|
||||
glPushMatrix();
|
||||
glScalef(1, 1.0F / m_rendererCtx->getSelectedCount(), 1);
|
||||
for (size_t i = 0; i < m_rendererCtx->getSelectedCount(); ++i)
|
||||
{
|
||||
glPushAttrib(GL_ALL_ATTRIB_BITS);
|
||||
glPushMatrix();
|
||||
glColor4f(m_color.r, m_color.g, m_color.b, m_rendererCtx->getTranslucency());
|
||||
glTranslatef(0, float(m_rendererCtx->getSelectedCount() - i) - 1.0F, 0);
|
||||
if (!m_rotate)
|
||||
{
|
||||
glScalef(1, -1, 1);
|
||||
glRotatef(-90, 0, 0, 1);
|
||||
}
|
||||
m_subRendererCtx->setAspect(m_rendererCtx->getAspect());
|
||||
m_subRendererCtx->setStackCount(m_rendererCtx->getSelectedCount());
|
||||
m_subRendererCtx->setStackIndex(i);
|
||||
m_renderers[m_rendererCtx->getSelected(i)]->render(*m_subRendererCtx);
|
||||
|
||||
/*
|
||||
if (0) //if (bDrawBorders)
|
||||
{
|
||||
glDisable(GL_TEXTURE_1D);
|
||||
glDisable(GL_BLEND);
|
||||
glColor3f(0, 0, 0);
|
||||
glBegin(GL_LINE_LOOP);
|
||||
glVertex2f(0, 0);
|
||||
glVertex2f(1, 0);
|
||||
glVertex2f(1, 1);
|
||||
glVertex2f(0, 1);
|
||||
glEnd();
|
||||
}
|
||||
*/
|
||||
glPopMatrix();
|
||||
glPopAttrib();
|
||||
}
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
StreamRendererSpectrum::postDraw();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererSpectrum::preDraw()
|
||||
{
|
||||
this->updateRulerVisibility();
|
||||
|
||||
// auto m_sColorGradient=CString("0:0,0,0; 100:100,100,100");
|
||||
const std::string gradient =
|
||||
"0:100, 100, 100; 12:50, 100, 100; 25:0, 50, 100; 38:0, 0, 50; 50:0, 0, 0; 62:50, 0, 0; 75:100, 50, 0; 88:100, 100, 50; 100:100, 100, 100";
|
||||
|
||||
if (!m_textureID) { m_textureID = m_gtkGLWidget.createTexture(gradient); }
|
||||
glBindTexture(GL_TEXTURE_1D, m_textureID);
|
||||
|
||||
m_rendererCtx->setAspect(m_viewport->allocation.width * 1.0F / m_viewport->allocation.height);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererSpectrum::finalize()
|
||||
{
|
||||
for (size_t i = 0; i < m_renderers.size(); ++i)
|
||||
{
|
||||
m_renderers[i]->rebuild(*m_subRendererCtx);
|
||||
m_renderers[i]->refresh(*m_subRendererCtx);
|
||||
}
|
||||
redraw(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CString StreamRendererSpectrum::renderAsText(const size_t indent) const
|
||||
{
|
||||
auto& hdr = m_stream->getHeader();
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::string(indent, ' ') << "Sampling rate: " << hdr.m_Sampling << "hz" << std::endl;
|
||||
ss << std::string(indent, ' ') << "Channels: " << hdr.m_Header.getDimensionSize(0) << std::endl;
|
||||
ss << std::string(indent, ' ') << "Abscissas per spectrum: " << hdr.m_Abscissas.getBufferElementCount() << std::endl;
|
||||
|
||||
// ss << std::string(indent, ' ') << "Channels: " << m_Header.m_header.getDimensionSize(0) << std::endl;
|
||||
// ss << std::string(indent, ' ') << "Samples per chunk: " << m_Header.m_header.getDimensionSize(1) << std::endl;
|
||||
return ss.str().c_str();
|
||||
}
|
||||
|
||||
bool StreamRendererSpectrum::mouseButton(const int x, const int y, const int button, const int status)
|
||||
{
|
||||
//if (button == 3 && status == 1) { showChunkList(); }
|
||||
return StreamRendererBase::mouseButton(x, y, button, status);
|
||||
}
|
||||
|
||||
bool StreamRendererSpectrum::showChunkList() { return showMatrixList<TypeSpectrum>(m_stream, &m_streamListWindow, "List of chunks for Spectrum stream"); }
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+207
@@ -0,0 +1,207 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
// @author J.T. Lindgren
|
||||
//
|
||||
// @note This renderer is intended to be used when there is only
|
||||
// a stimulation track. More often, the Tracker users might want to
|
||||
// see the stimulations overlayed on the signal track.
|
||||
//
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <mensia/advanced-visualization.hpp>
|
||||
#include <mTGtkGLWidget.hpp>
|
||||
#include <m_GtkGL.hpp>
|
||||
|
||||
#include "StreamRendererStimulation.h"
|
||||
#include "TypeStimulation.h"
|
||||
|
||||
#include "ruler/mTRulerAutoType.hpp"
|
||||
#include "ruler/mTRulerPair.hpp"
|
||||
#include "ruler/mTRulerConditionalPair.hpp"
|
||||
#include "ruler/mCRulerConditionIsTimeLocked.hpp"
|
||||
|
||||
#include "ruler/mCRulerProgressV.hpp"
|
||||
|
||||
#include "ruler/mCRulerBottomCount.hpp"
|
||||
#include "ruler/mCRulerBottomTime.hpp"
|
||||
|
||||
#include "ruler/mCRulerLeftChannelNames.hpp"
|
||||
|
||||
#include "ruler/mCRulerRightCount.hpp"
|
||||
#include "ruler/mCRulerRightScale.hpp"
|
||||
#include "ruler/mCRulerRightLabels.hpp"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
bool StreamRendererStimulation::initialize()
|
||||
{
|
||||
m_nChannel = 1;
|
||||
m_sampling = 512;
|
||||
m_samplesPerChunk = 32;
|
||||
m_chunkDuration = CTime(m_sampling, m_samplesPerChunk);
|
||||
|
||||
// TRendererStimulation < false, CRendererLine >:new CRendererLine
|
||||
AdvancedVisualization::IRenderer* renderer = AdvancedVisualization::IRenderer::create(AdvancedVisualization::ERendererType::Line, true);
|
||||
if (renderer == nullptr) { return false; }
|
||||
|
||||
// Creates renderer context
|
||||
m_rendererCtx = new AdvancedVisualization::CRendererContext();
|
||||
m_rendererCtx->clear();
|
||||
m_rendererCtx->setTimeScale(1);
|
||||
m_rendererCtx->setCheckBoardVisibility(true);
|
||||
m_rendererCtx->setScaleVisibility(m_isScaleVisible);
|
||||
m_rendererCtx->setDataType(AdvancedVisualization::CRendererContext::EDataType::Signal);
|
||||
m_rendererCtx->addChannel("Stims");
|
||||
m_rendererCtx->setSampleDuration(CTime(m_sampling, 1).time());
|
||||
|
||||
m_ruler = new AdvancedVisualization::TRulerPair<AdvancedVisualization::CRulerProgressV, AdvancedVisualization::TRulerPair<
|
||||
AdvancedVisualization::TRulerAutoType<
|
||||
AdvancedVisualization::IRuler, AdvancedVisualization::TRulerConditionalPair<
|
||||
AdvancedVisualization::CRulerBottomTime, AdvancedVisualization::CRulerBottomCount,
|
||||
AdvancedVisualization::CRulerConditionIsTimeLocked>, AdvancedVisualization::IRuler>,
|
||||
AdvancedVisualization::TRulerPair<
|
||||
AdvancedVisualization::CRulerLeftChannelNames, AdvancedVisualization::CRulerRightScale>>>;
|
||||
|
||||
m_ruler->setRendererContext(m_rendererCtx);
|
||||
m_ruler->setRenderer(renderer);
|
||||
|
||||
if (!StreamRendererBase::initialize()) { return false; }
|
||||
|
||||
m_gtkGLWidget.initialize(*this, m_viewport, m_left, m_right, m_bottom);
|
||||
m_gtkGLWidget.setPointSmoothingActive(false);
|
||||
|
||||
m_renderers.push_back(renderer);
|
||||
|
||||
gtk_widget_set_size_request(m_main, 640, 100);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererStimulation::reset(const CTime startTime, const CTime endTime)
|
||||
{
|
||||
m_startTime = startTime;
|
||||
m_endTime = endTime;
|
||||
|
||||
// std::cout << "Overridden stimulation renderer reset\n";
|
||||
|
||||
m_renderers[0]->clear(0);
|
||||
|
||||
const uint64_t chunkCount = (m_endTime - m_startTime).time() / m_chunkDuration.time();
|
||||
const uint32_t numSamples = uint32_t(m_samplesPerChunk * chunkCount);
|
||||
|
||||
m_renderers[0]->setChannelCount(m_nChannel);
|
||||
m_renderers[0]->setSampleCount(numSamples);
|
||||
|
||||
// @FIXME The offset is needed to have correct numbers on the ruler; remove ifdef once the feature is in
|
||||
#ifdef RENDERER_SUPPORTS_OFFSET
|
||||
m_renderers[0]->setTimeOffset(m_startTime.time());
|
||||
#endif
|
||||
|
||||
// Stick in empty chunks to get a background
|
||||
std::vector<float> empty;
|
||||
empty.resize(m_samplesPerChunk * 1, 0); // 1 channel
|
||||
for (uint64_t i = 0; i < chunkCount; ++i) { m_renderers[0]->feed(&empty[0], m_samplesPerChunk); }
|
||||
|
||||
m_renderers[0]->rebuild(*m_rendererCtx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererStimulation::push(const TypeStimulation::Buffer& chunk, bool /*zeroInput*/)
|
||||
{
|
||||
for (size_t i = 0; i < chunk.m_buffer.getStimulationCount(); ++i)
|
||||
{
|
||||
m_renderers[0]->feed(chunk.m_buffer.getStimulationDate(i) - m_startTime.time(), chunk.m_buffer.getStimulationIdentifier(i));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CString StreamRendererStimulation::renderAsText(const size_t /*indent*/) const
|
||||
{
|
||||
// No specific details for stimulation streams
|
||||
return "";
|
||||
}
|
||||
|
||||
bool StreamRendererStimulation::showChunkList()
|
||||
{
|
||||
if (m_stimulationListWindow)
|
||||
{
|
||||
gtk_window_present(GTK_WINDOW(m_stimulationListWindow));
|
||||
return true;
|
||||
}
|
||||
|
||||
GtkBuilder* pBuilder = gtk_builder_new();
|
||||
const CString filename = Directories::getDataDir() + "/applications/tracker/tracker.ui";
|
||||
if (!gtk_builder_add_from_file(pBuilder, filename, nullptr))
|
||||
{
|
||||
std::cout << "Problem loading [" << filename << "]\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_stimulationListWindow = GTK_WIDGET(gtk_builder_get_object(pBuilder, "tracker-stimulation_list"));
|
||||
GtkTreeView* channelTreeView = GTK_TREE_VIEW(gtk_builder_get_object(pBuilder, "tracker-stimulation_list-treeview"));
|
||||
// GtkListStore* channelListStore = GTK_LIST_STORE(gtk_builder_get_object(pBuilder, "liststore_select"));
|
||||
GtkTreeStore* channelListStore = gtk_tree_store_new(7, G_TYPE_UINT, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_UINT64, G_TYPE_STRING,
|
||||
G_TYPE_DOUBLE);
|
||||
|
||||
gtk_window_set_title(GTK_WINDOW(m_stimulationListWindow), "List of Stimulations in the stream");
|
||||
|
||||
add_column(channelTreeView, "Chunk#", 0, 20);
|
||||
add_column(channelTreeView, "ChunkStart (s)", 1, 10);
|
||||
add_column(channelTreeView, "ChunkEnd (s)", 2, 10);
|
||||
add_column(channelTreeView, "Stim Time (s)", 3, 20);
|
||||
add_column(channelTreeView, "Stim Id", 4, 5);
|
||||
add_column(channelTreeView, "Stim Name", 5, 40);
|
||||
add_column(channelTreeView, "Stim Duration", 6, 10);
|
||||
|
||||
gtk_tree_view_set_model(channelTreeView, GTK_TREE_MODEL(channelListStore));
|
||||
|
||||
GtkTreeIter it;
|
||||
gtk_tree_store_clear(channelListStore);
|
||||
|
||||
// ::gtk_tree_view_set_model(m_pChannelTreeView, nullptr);
|
||||
for (size_t i = 0; i < m_stream->getChunkCount(); ++i)
|
||||
{
|
||||
const TypeStimulation::Buffer* ptr = m_stream->getChunk(i);
|
||||
if (!ptr) { break; }
|
||||
|
||||
for (uint32_t s = 0; s < ptr->m_buffer.getStimulationCount(); ++s)
|
||||
{
|
||||
const CString stimName = m_kernelCtx.getTypeManager().getEnumerationEntryNameFromValue(
|
||||
OV_TypeId_Stimulation, ptr->m_buffer.getStimulationIdentifier(s));
|
||||
|
||||
gtk_tree_store_append(channelListStore, &it, nullptr);
|
||||
gtk_tree_store_set(channelListStore, &it,
|
||||
0, i,
|
||||
1, ptr->m_StartTime.toSeconds(),
|
||||
2, ptr->m_EndTime.toSeconds(),
|
||||
3, CTime(ptr->m_buffer.getStimulationDate(s)).toSeconds(),
|
||||
4, ptr->m_buffer.getStimulationIdentifier(s),
|
||||
5, stimName.toASCIIString(),
|
||||
6, CTime(ptr->m_buffer.getStimulationDuration(s)).toSeconds(), -1);
|
||||
}
|
||||
}
|
||||
|
||||
// GList* cols = gtk_tree_view_get_columns(m_pChannelTreeView);
|
||||
|
||||
// Hide instead of destroy on closing the window
|
||||
g_signal_connect(m_stimulationListWindow, "delete_event", G_CALLBACK(gtk_widget_hide_on_delete), nullptr);
|
||||
gtk_widget_show_all(GTK_WIDGET(m_stimulationListWindow));
|
||||
g_object_unref(pBuilder);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamRendererStimulation::mouseButton(const int x, const int y, const int button, const int status)
|
||||
{
|
||||
// if (button == 3 && status == 1) { showStimulationList(); }
|
||||
return StreamRendererBase::mouseButton(x, y, button, status);
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// OpenViBE Tracker
|
||||
//
|
||||
// @author J.T. Lindgren
|
||||
//
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "Testclass.h"
|
||||
// #include "Source.h"
|
||||
#include "Stream.h"
|
||||
#include "Encoder.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
TestClass::TestClass(Kernel::IKernelContext& ctx) : m_Ctx(ctx)
|
||||
{
|
||||
std::cout << "Testing\n";
|
||||
|
||||
// Encoder2<TypeMatrix::Header, TypeMatrix::Buffer, TypeMatrix::End> testCoder(ctx);
|
||||
// Encoder<TypeMatrix> testCoder(ctx);
|
||||
|
||||
#if 0
|
||||
StreamSignal testStream(ctx);
|
||||
StreamMatrix testStream2(ctx);
|
||||
|
||||
std::vector<Stream*> list;
|
||||
|
||||
list.push_back(&testStream);
|
||||
list.push_back(&testStream2);
|
||||
|
||||
Stream* tmp = list[0];
|
||||
|
||||
std::cout << "First is " << ctx.getTypeManager().getTypeName(tmp->getTypeIdentifier()) << "\n";
|
||||
#endif
|
||||
|
||||
//const CString eegFile = Directories::getDataDir() + CString("/scenarios/signals/bci-motor-imagery.ov");
|
||||
//Source src;
|
||||
//src.initialize(eegFile.toASCIIString());
|
||||
|
||||
#if 0
|
||||
// Test code illustrating how to alter stimulation stream
|
||||
for (auto it = m_Streams.begin();it != m_Streams.end(); ++it)
|
||||
{
|
||||
if(it->second->getTypeIdentifier() == OV_TypeId_Stimulations)
|
||||
{
|
||||
TypeError::Buffer *ptr = nullptr;
|
||||
it->second->peek(CTime(5.0).time(), &ptr);
|
||||
TypeStimulation::Buffer *ptr2 = reinterpret_cast<TypeStimulation::Buffer*>(ptr);
|
||||
// std::cout << "cnt: " << ptr2->m_buffer.getStimulationCount() << "\n";
|
||||
|
||||
// Request early stop
|
||||
ptr2->m_buffer.clear();
|
||||
ptr2->m_buffer.appendStimulation(OVTK_StimulationId_ExperimentStop, CTime(5.0).time(),0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
StreamHeaderSignal signalHeader;
|
||||
signalHeader.m_samplingRate = 512;
|
||||
|
||||
Stream<StreamHeaderSignal, StreamDataSignal> testStream(m_ctx);
|
||||
testStream.initialize(signalHeader);
|
||||
|
||||
StreamDataSignal* data = new(StreamDataSignal);
|
||||
data->data.setDimensionSize(10,2);
|
||||
testStream.push(data);
|
||||
|
||||
// Encode a stream
|
||||
Encoder<StreamHeaderSignal, StreamDataSignal> encoder(ctx);
|
||||
|
||||
std::vector<CMemoryBuffer> encoded;
|
||||
|
||||
encoded.push_back( encoder.encodeHeader(testStream.getHeader()) );
|
||||
|
||||
for (size_t i=0;i<testStream.m_chunks.size(); ++i) { encoded.push_back( encoder.encodeBuffer(*testStream.m_chunks[i]) ); }
|
||||
|
||||
Stream* ptr = testStream;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Executable
+53
@@ -0,0 +1,53 @@
|
||||
#include "TrackerPluginChannelCheck.h"
|
||||
|
||||
#include "Stream.h"
|
||||
#include "TypeSignal.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
bool TrackerPluginChannelCheck::process(Workspace& wp, ParallelExecutor& /* exec */)
|
||||
{
|
||||
log() << Kernel::LogLevel_Info << "TrackerPluginChannelCheck: Testing if all selected signal streams have the same number of channels ...\n";
|
||||
|
||||
uint32_t nChannels = 0;
|
||||
size_t nTested = 0;
|
||||
|
||||
for (size_t t = 0; t < wp.getNumTracks(); ++t)
|
||||
{
|
||||
const auto& track = wp.getTrack(t);
|
||||
|
||||
for (size_t s = 0; s < track->getNumStreams(); ++s)
|
||||
{
|
||||
const auto stream = track->getStream(s);
|
||||
|
||||
// This plugin only handles signal streams
|
||||
if (stream->getSelected() && stream->getTypeIdentifier() == OV_TypeId_Signal)
|
||||
{
|
||||
nTested++;
|
||||
|
||||
auto typedStream = std::static_pointer_cast<Stream<TypeSignal>>(stream);
|
||||
|
||||
const auto& hdr = typedStream->getHeader();
|
||||
const uint32_t thisChannels = hdr.m_Header.getDimensionSize(0);
|
||||
if (nTested == 1) { nChannels = thisChannels; }
|
||||
else if (nChannels != thisChannels)
|
||||
{
|
||||
log() << Kernel::LogLevel_Error << "Stream " << (s + 1) << " of Track " << t + 1 << " has different number of signal channels ("
|
||||
<< thisChannels << ") than previous signal streams (" << nChannels << ")\n";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This plugin does not modify the track. If it did, we would need to call the following:
|
||||
// track.setDirtyBit(true);
|
||||
}
|
||||
|
||||
log() << Kernel::LogLevel_Info << "All signal streams have the same amount of channels (" << nTested << " tested)\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
#include <memory> // shared_ptr
|
||||
|
||||
#include "TrackerPluginCountStimulations.h"
|
||||
|
||||
#include "Stream.h"
|
||||
#include "TypeStimulation.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
bool TrackerPluginCountStimulations::process(StreamBundle& track)
|
||||
{
|
||||
log() << Kernel::LogLevel_Info << "TrackerPluginCountStimulation: Counting stimulations in the selected streams\n";
|
||||
|
||||
bool processedSomething = false;
|
||||
|
||||
for (size_t s = 0; s < track.getNumStreams(); ++s)
|
||||
{
|
||||
const auto stream = track.getStream(s);
|
||||
|
||||
// This plugin only handles stimulation streams
|
||||
if (stream->getSelected() && stream->getTypeIdentifier() == OV_TypeId_Stimulations)
|
||||
{
|
||||
processedSomething = true;
|
||||
|
||||
std::map<uint64_t, uint64_t> histogram;
|
||||
double numStims = 0;
|
||||
|
||||
const auto typedStream = std::static_pointer_cast<const Stream<TypeStimulation>>(stream);
|
||||
|
||||
// Count occurrences
|
||||
for (size_t i = 0; i < typedStream->getChunkCount(); ++i)
|
||||
{
|
||||
const auto chunk = typedStream->getChunk(i);
|
||||
for (size_t stimIdx = 0; stimIdx < chunk->m_buffer.getStimulationCount(); ++stimIdx)
|
||||
{
|
||||
auto id = chunk->m_buffer.getStimulationIdentifier(stimIdx);
|
||||
|
||||
auto it = histogram.find(id);
|
||||
if (it != histogram.end()) { it->second++; }
|
||||
else { histogram[id] = 1; }
|
||||
numStims++;
|
||||
}
|
||||
}
|
||||
|
||||
// Print
|
||||
log() << Kernel::LogLevel_Info << "Stimulation counts for stream " << (s + 1) << " ...\n";
|
||||
for (auto& it : histogram)
|
||||
{
|
||||
const CString name = m_kernelCtx.getTypeManager().getEnumerationEntryNameFromValue(OV_TypeId_Stimulation, it.first);
|
||||
|
||||
log() << Kernel::LogLevel_Info << (name.length() > 0 ? name : "Unregistered") << " (" << it.first << ") " << " : " << it.second
|
||||
<< " (" << (it.second / numStims) * 100 << "%)" << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This plugin does not modify the track. If it did, we would need to call the following:
|
||||
// track.setDirtyBit(true);
|
||||
|
||||
if (!processedSomething) { log() << Kernel::LogLevel_Info << "No selected streams compatible with stimulation counting plugin\n"; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
#include <algorithm> // std::sort
|
||||
|
||||
#include "TrackerPlugins.h"
|
||||
#include "TrackerPluginCountStimulations.h"
|
||||
#include "TrackerPluginChannelCheck.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Tracker {
|
||||
|
||||
TrackerPlugins::TrackerPlugins(const Kernel::IKernelContext& ctx) : Contexted(ctx)
|
||||
{
|
||||
// Declare all plugins here
|
||||
m_pluginCreateCalls.push_back([&ctx]() { return new TrackerPluginCountStimulations(ctx); });
|
||||
m_pluginCreateCalls.push_back([&ctx]() { return new TrackerPluginChannelCheck(ctx); });
|
||||
|
||||
// Create example instances of the plugins so the GUI can display a list
|
||||
for (auto& fun : m_pluginCreateCalls)
|
||||
{
|
||||
auto ptr = fun();
|
||||
m_trackerPlugins.push_back(ptr);
|
||||
}
|
||||
|
||||
if (m_trackerPlugins.empty()) { return; }
|
||||
|
||||
// get both arrays sorted. No code beauty contest winners here...
|
||||
std::vector<size_t> indexes;
|
||||
for (size_t i = 0; i < m_trackerPlugins.size(); ++i) { indexes.push_back(i); }
|
||||
|
||||
auto& pluginRef = m_trackerPlugins;
|
||||
auto& callRef = m_pluginCreateCalls;
|
||||
|
||||
std::sort(indexes.begin(), indexes.end(),
|
||||
[&pluginRef](const size_t a, const size_t b) { return (pluginRef[a]->getName()) < (pluginRef[b]->getName()); });
|
||||
|
||||
std::vector<ITrackerPlugin*> sortedPlugins;
|
||||
std::transform(indexes.begin(), indexes.end(), std::back_inserter(sortedPlugins), [pluginRef](const size_t i) { return pluginRef[i]; });
|
||||
m_trackerPlugins = sortedPlugins;
|
||||
|
||||
std::vector<std::function<ITrackerPlugin*()>> sortedCalls;
|
||||
std::transform(indexes.begin(), indexes.end(), std::back_inserter(sortedCalls), [callRef](const size_t i) { return callRef[i]; });
|
||||
m_pluginCreateCalls = sortedCalls;
|
||||
}
|
||||
|
||||
TrackerPlugins::~TrackerPlugins()
|
||||
{
|
||||
for (auto f : m_trackerPlugins) { delete f; }
|
||||
m_trackerPlugins.clear();
|
||||
m_pluginCreateCalls.clear();
|
||||
}
|
||||
|
||||
ITrackerPlugin* TrackerPlugins::getPluginCopy(const size_t index) const
|
||||
{
|
||||
if (index >= m_pluginCreateCalls.size()) { return nullptr; }
|
||||
const auto fun = m_pluginCreateCalls[index];
|
||||
return fun();
|
||||
}
|
||||
|
||||
} // namespace Tracker
|
||||
} // namespace OpenViBE
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user