This commit is contained in:
2021-10-14 13:47:35 +02:00
commit 6625a8dfaa
4026 changed files with 844291 additions and 0 deletions
@@ -0,0 +1,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
@@ -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
@@ -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
@@ -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;
};
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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