diff --git a/src/TactileStimulation/ovpCBoxAlgorithmTactileStimulator.cpp b/src/TactileStimulation/ovpCBoxAlgorithmTactileStimulator.cpp
new file mode 100644
index 0000000..003076f
--- /dev/null
+++ b/src/TactileStimulation/ovpCBoxAlgorithmTactileStimulator.cpp
@@ -0,0 +1,128 @@
+///-------------------------------------------------------------------------------------------------
+///
+/// \file ovpCBoxAlgorithmTactileStimulator.cpp
+/// \brief Functions of the Box Tactile Stimulator.
+/// \author Tobias Baumann (TH-Nürnberg).
+/// \version 1.0.
+/// \date Mon Feb 21 14:59:56 2022.
+/// \copyright GNU Affero General Public License v3.0.
+///
+///-------------------------------------------------------------------------------------------------
+
+//includes
+#include "ovpCBoxAlgorithmTactileStimulation.h"
+
+using namespace OpenViBE;
+using namespace /*OpenViBE::*/Kernel;
+using namespace /*OpenViBE::*/Plugins;
+using namespace /*OpenViBE::Plugins::*/Tactilebci;
+
+bool CBoxAlgorithmTactileStimulation::initialize()
+{
+ m_input0Decoder.initialize(*this, 0);
+ m_output0Encoder.initialize(*this, 0);
+
+ // If you need to retrieve setting values, use the FSettingValueAutoCast function.
+ // For example :
+ // - CString setting at index 0 in the setting list :
+ // CString sSettingValue = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
+ // - unsigned int64 setting at index 1 in the setting list :
+ // uint64_t uiSettingValue = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
+ // - double setting at index 2 in the setting list :
+ // double doubleSettingValue = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2);
+ // ...
+
+ return true;
+}
+/*******************************************************************************/
+
+bool CBoxAlgorithmTactileStimulation::uninitialize()
+{
+ m_input0Decoder.uninitialize();
+ m_output0Encoder.uninitialize();
+
+ return true;
+}
+/*******************************************************************************/
+
+
+bool CBoxAlgorithmTactileStimulation::process()
+{
+
+ // the static box context describes the box inputs, outputs, settings structures
+ const IBox& staticBoxContext=this->getStaticBoxContext();
+ // the dynamic box context describes the current state of the box inputs and outputs (i.e. the chunks)
+ IBoxIO& boxContext = this->getDynamicBoxContext();
+
+ // here is some useful functions:
+ // - To get input/output/setting count:
+ // staticBoxContext.getInputCount();
+ // staticBoxContext.getOutputCount();
+
+ // - To get the number of chunks currently available on a particular input :
+ // boxContext.getInputChunkCount(input_index)
+
+ // - To send an output chunk :
+ // boxContext.markOutputAsReadyToSend(output_index, chunk_start_time, chunk_end_time);
+
+
+ // A typical process iteration may look like this.
+ // This example only iterate over the first input of type Signal, and output a modified Signal.
+ // thus, the box uses 1 decoder (m_signalDecoder) and 1 encoder (m_signalEncoder)
+ /*
+ IBoxIO& boxContext = this->getDynamicBoxContext();
+
+ //iterate over all chunk on input 0
+ for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
+ {
+ // decode the chunk i
+ m_signalDecoder.decode(i);
+ // the decoder may have decoded 3 different parts : the header, a buffer or the end of stream.
+ if(m_signalDecoder.isHeaderReceived())
+ {
+ // Header received. This happens only once when pressing "play". For example with a StreamedMatrix input, you now know the dimension count, sizes, and labels of the matrix
+ // ... maybe do some process ...
+
+ // Pass the header to the next boxes, by encoding a header on the output 0:
+ m_signalEncoder.encodeHeader(0);
+ // send the output chunk containing the header. The dates are the same as the input chunk:
+ boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
+ }
+ if(m_signalDecoder.isBufferReceived())
+ {
+ // Buffer received. For example the signal values
+ // Access to the buffer can be done thanks to :
+ CMatrix* matrix = m_signalDecoder.getOutputMatrix(); // the StreamedMatrix of samples.
+ uint64_t frequency = m_signalDecoder.getOutputSamplingRate(); // the sampling rate of the signal
+
+ // ... do some process on the matrix ...
+
+ // Encode the output buffer :
+ m_signalEncoder.encodeBuffer(0);
+ // and send it to the next boxes :
+ boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
+
+ }
+ if(m_signalDecoder.isEndReceived())
+ {
+ // End of stream received. This happens only once when pressing "stop". Just pass it to the next boxes so they receive the message :
+ m_signalEncoder.encodeEnd(0);
+ boxContext.markOutputAsReadyToSend(0, boxContext.getInputChunkStartTime(0, i), boxContext.getInputChunkEndTime(0, i));
+ }
+
+ // The current input chunk has been processed, and automaticcaly discarded.
+ // you don't need to call "boxContext.markInputAsDeprecated(0, i);"
+ }
+ */
+
+ // check the official developer documentation webpage for more example and information :
+
+ // Tutorials:
+ // http://openvibe.inria.fr/documentation/#Developer+Documentation
+ // Codec Toolkit page :
+ // http://openvibe.inria.fr/codec-toolkit-references/
+
+ // Feel free to ask experienced developers on the forum (http://openvibe.inria.fr/forum) and IRC (#openvibe on irc.freenode.net).
+
+ return true;
+}
diff --git a/src/TactileStimulation/ovpCBoxAlgorithmTactileStimulator.h b/src/TactileStimulation/ovpCBoxAlgorithmTactileStimulator.h
new file mode 100644
index 0000000..207b654
--- /dev/null
+++ b/src/TactileStimulation/ovpCBoxAlgorithmTactileStimulator.h
@@ -0,0 +1,107 @@
+///-------------------------------------------------------------------------------------------------
+///
+/// \file ovpCBoxAlgorithmTactileStimulator.h
+/// \brief Classes of the Box Tactile Stimulator.
+/// \author Tobias Baumann (TH-Nürnberg).
+/// \version 1.0.
+/// \date Mon Feb 21 14:59:56 2022.
+/// \copyright GNU Affero General Public License v3.0.
+///
+///-------------------------------------------------------------------------------------------------
+
+//includes
+#pragma once
+
+#include "../ovp_defines.h"
+#include
+#include
+#include
+
+namespace OpenViBE
+{
+ namespace Plugins
+ {
+ namespace Tactilebci
+ {
+ /// The class CBoxAlgorithmTactileStimulator describes the box Tactile Stimulator.
+ class CBoxAlgorithmTactileStimulation final : virtual public Toolkit::TBoxAlgorithm
+ {
+ public:
+ void release() override { delete this; }
+
+ bool initialize() override;
+ bool uninitialize() override;
+
+ //Here is the different process callbacks possible
+ // - On clock ticks :
+ //bool processClock(Kernel::CMessageClock& msg) override;
+ // - On new input received (the most common behaviour for signal processing) :
+ //bool processInput(const size_t index) override;
+
+ // If you want to use processClock, you must provide the clock frequency.
+ //uint64_t getClockFrequency() override;
+
+ bool process() override;
+
+ // As we do with any class in openvibe, we use the macro below to associate this box to an unique identifier.
+ // The inheritance information is also made available, as we provide the superclass Toolkit::TBoxAlgorithm < IBoxAlgorithm >
+ _IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm, OVP_ClassId_BoxAlgorithm_TactileStimulation)
+
+ protected:
+ // Input decoder:
+ Toolkit::TStimulationDecoder m_input0Decoder;
+ // Output decoder:
+ Toolkit::TStimulationEncoder m_output0Encoder;
+
+ };
+
+ /// Descriptor of the box Tactile Stimulator.
+ class CBoxAlgorithmTactileStimulationDesc final : virtual public IBoxAlgorithmDesc
+ {
+ public:
+
+ void release() override { }
+
+ CString getName() const override { return CString("Tactile Stimulation"); }
+ CString getAuthorName() const override { return CString("Tobias Baumann"); }
+ CString getAuthorCompanyName() const override { return CString("TH-Nürnberg"); }
+ CString getShortDescription() const override { return CString("Controls the Tactilos on the Lattepanda GPIOs"); }
+ CString getDetailedDescription() const override { return CString(""); }
+ CString getCategory() const override { return CString("TactileBCI"); }
+ CString getVersion() const override { return CString("1.0"); }
+ CString getStockItemName() const override { return CString("gtk-network"); }
+
+ CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_TactileStimulator; }
+ IPluginObject* create() override { return new CBoxAlgorithmTactileStimulator; }
+
+ /*
+ IBoxListener* createBoxListener() const override { return new CBoxAlgorithmTactileStimulatorListener; }
+ void releaseBoxListener(IBoxListener* listener) const override { delete listener; }
+ */
+ bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
+ {
+ prototype.addInput("StimInput",OV_TypeId_Stimulations);
+
+ //prototype.addFlag(Kernel::BoxFlag_CanModifyInput);
+ //prototype.addFlag(Kernel::BoxFlag_CanAddInput);
+
+ prototype.addOutput("StimOutput",OV_TypeId_Stimulations);
+
+ //prototype.addFlag(Kernel::BoxFlag_CanModifyOutput);
+ //prototype.addFlag(Kernel::BoxFlag_CanAddOutput);
+
+ //No setting specified.To add settings use :
+ //prototype.addSetting("SettingName",OV_TypeId_XXXX,"default value");
+
+ //prototype.addFlag(Kernel::BoxFlag_CanModifySetting);
+ //prototype.addFlag(Kernel::BoxFlag_CanAddSetting);
+
+ prototype.addFlag(OV_AttributeId_Box_FlagIsUnstable);
+
+ return true;
+ }
+ _IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_TactileStimulationDesc)
+ };
+ } // namespace Tactilebci
+ } // namespace Plugins
+} // namespace OpenViBE