Commit 48afa476 authored by Jussi Lindgren's avatar Jussi Lindgren Committed by Serrière Guillaume

Plugins: Misc fixes to the TCP writer box

- Fixed handling of 1-dimensional matrices in TCPWriter
- Removed all transposes of the outputs in TCPWriter
- Removed incorrect setting modify flag in TCPWriter
- Fixed the example client buffersize to chunk size to reduce delays with tiny chunks
- Changed the example client to allow reading either stim, signal or both, and write signal to file
- Minor code cleanup
parent 0a38e128
......@@ -56,7 +56,7 @@
</Attribute>
<Attribute>
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
<Value>(0x00000000, 0x02017c54)</Value>
<Value>(0x00000000, 0x015d7484)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
......@@ -68,6 +68,79 @@
</Attribute>
</Attributes>
</Box>
<Box>
<Identifier>(0x00002e3e, 0x00002059)</Identifier>
<Name>Stimuli to TCP</Name>
<AlgorithmClassIdentifier>(0x02f24947, 0x17fa0477)</AlgorithmClassIdentifier>
<Inputs>
<Input>
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
<Name>Input 1</Name>
</Input>
</Inputs>
<Settings>
<Setting>
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
<Name>Port</Name>
<DefaultValue>5678</DefaultValue>
<Value>5679</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x6d7e53dd, 0x6a0a4753)</TypeIdentifier>
<Name>Stimulus output</Name>
<DefaultValue>Raw</DefaultValue>
<Value>String</Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>400.000000</Value>
</Attribute>
<Attribute>
<Identifier>(0x1fa963f5, 0x1a638cd4)</Identifier>
<Value>53</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>480.000000</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0xd1b2ff20, 0x919e4441)</Value>
</Attribute>
<Attribute>
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
<Value></Value>
</Attribute>
<Attribute>
<Identifier>(0xad100179, 0xa3c984ab)</Identifier>
<Value>100</Value>
</Attribute>
<Attribute>
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
<Value>(0x00000000, 0x016b97f3)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc67a01dc, 0x28ce06c1)</Identifier>
<Value></Value>
</Attribute>
<Attribute>
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
<Value>false</Value>
</Attribute>
<Attribute>
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
<Value>2</Value>
</Attribute>
<Attribute>
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
<Value>1</Value>
</Attribute>
</Attributes>
</Box>
<Box>
<Identifier>(0x0000360b, 0x0000614e)</Identifier>
<Name>Signal display</Name>
......@@ -178,7 +251,7 @@
</Attribute>
<Attribute>
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
<Value>(0x00000000, 0x033e3986)</Value>
<Value>(0x00000000, 0x01e683c6)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
......@@ -236,7 +309,7 @@
</Attribute>
<Attribute>
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
<Value>(0x00000000, 0x02dff832)</Value>
<Value>(0x00000000, 0x01b4ded6)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc80ce8af, 0xf699f813)</Identifier>
......@@ -249,7 +322,7 @@
</Attributes>
</Box>
<Box>
<Identifier>(0x00004e8d, 0x00003806)</Identifier>
<Identifier>(0x000054a6, 0x00001c9c)</Identifier>
<Name>Signal to TCP</Name>
<AlgorithmClassIdentifier>(0x02f24947, 0x17fa0477)</AlgorithmClassIdentifier>
<Inputs>
......@@ -267,7 +340,7 @@
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x6d7e53dd, 0x6a0a4753)</TypeIdentifier>
<TypeIdentifier>(0x77d3e238, 0xb954ec48)</TypeIdentifier>
<Name>Stimulus output</Name>
<DefaultValue>Raw</DefaultValue>
<Value>Raw</Value>
......@@ -289,7 +362,7 @@
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0xb3ff8941, 0x4943ae4b)</Value>
<Value>(0xd1b2ff20, 0x919e4441)</Value>
</Attribute>
<Attribute>
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
......@@ -301,80 +374,7 @@
</Attribute>
<Attribute>
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
<Value>(0x00000000, 0x019d6582)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
<Value>false</Value>
</Attribute>
<Attribute>
<Identifier>(0xce18836a, 0x9c0eb403)</Identifier>
<Value>2</Value>
</Attribute>
<Attribute>
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
<Value>1</Value>
</Attribute>
<Attribute>
<Identifier>(0xf191c1c8, 0xa0123976)</Identifier>
<Value></Value>
</Attribute>
</Attributes>
</Box>
<Box>
<Identifier>(0x00005ae2, 0x000004fd)</Identifier>
<Name>Stimuli to TCP</Name>
<AlgorithmClassIdentifier>(0x02f24947, 0x17fa0477)</AlgorithmClassIdentifier>
<Inputs>
<Input>
<TypeIdentifier>(0x6f752dd0, 0x082a321e)</TypeIdentifier>
<Name>Input 1</Name>
</Input>
</Inputs>
<Settings>
<Setting>
<TypeIdentifier>(0x007deef9, 0x2f3e95c6)</TypeIdentifier>
<Name>Port</Name>
<DefaultValue>5678</DefaultValue>
<Value>5679</Value>
<Modifiability>false</Modifiability>
</Setting>
<Setting>
<TypeIdentifier>(0x6d7e53dd, 0x6a0a4753)</TypeIdentifier>
<Name>Stimulus output</Name>
<DefaultValue>Raw</DefaultValue>
<Value>String</Value>
<Modifiability>false</Modifiability>
</Setting>
</Settings>
<Attributes>
<Attribute>
<Identifier>(0x1fa7a38f, 0x54edbe0b)</Identifier>
<Value>400.000000</Value>
</Attribute>
<Attribute>
<Identifier>(0x1fa963f5, 0x1a638cd4)</Identifier>
<Value>53</Value>
</Attribute>
<Attribute>
<Identifier>(0x207c9054, 0x3c841b63)</Identifier>
<Value>480.000000</Value>
</Attribute>
<Attribute>
<Identifier>(0x4e7b798a, 0x183beafb)</Identifier>
<Value>(0xb3ff8941, 0x4943ae4b)</Value>
</Attribute>
<Attribute>
<Identifier>(0x527ad68d, 0x16d746a0)</Identifier>
<Value></Value>
</Attribute>
<Attribute>
<Identifier>(0xad100179, 0xa3c984ab)</Identifier>
<Value>100</Value>
</Attribute>
<Attribute>
<Identifier>(0xc46b3d00, 0x3e0454e1)</Identifier>
<Value>(0x00000000, 0x027c9cc4)</Value>
<Value>(0x00000000, 0x00dda356)</Value>
</Attribute>
<Attribute>
<Identifier>(0xc73e83ec, 0xf855c5bc)</Identifier>
......@@ -388,23 +388,19 @@
<Identifier>(0xcfad85b0, 0x7c6d841c)</Identifier>
<Value>1</Value>
</Attribute>
<Attribute>
<Identifier>(0xf191c1c8, 0xa0123976)</Identifier>
<Value></Value>
</Attribute>
</Attributes>
</Box>
</Boxes>
<Links>
<Link>
<Identifier>(0x000027e0, 0x00000a8f)</Identifier>
<Identifier>(0x0000169e, 0x000043c2)</Identifier>
<Source>
<BoxIdentifier>(0x000047dd, 0x00002575)</BoxIdentifier>
<BoxIdentifier>(0x00002055, 0x00006c8d)</BoxIdentifier>
<BoxOutputIndex>0</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x0000360b, 0x0000614e)</BoxIdentifier>
<BoxInputIndex>1</BoxInputIndex>
<BoxIdentifier>(0x000054a6, 0x00001c9c)</BoxIdentifier>
<BoxInputIndex>0</BoxInputIndex>
</Target>
<Attributes>
<Attribute>
......@@ -413,26 +409,26 @@
</Attribute>
<Attribute>
<Identifier>(0x358ae8b5, 0x0f8bacd1)</Identifier>
<Value>448</Value>
<Value>224</Value>
</Attribute>
<Attribute>
<Identifier>(0x3f0a3b27, 0x570913d2)</Identifier>
<Value>283</Value>
<Value>369</Value>
</Attribute>
<Attribute>
<Identifier>(0x6267b5c5, 0x676e3e42)</Identifier>
<Value>352</Value>
<Value>208</Value>
</Attribute>
</Attributes>
</Link>
<Link>
<Identifier>(0x0000384a, 0x00007419)</Identifier>
<Identifier>(0x00002314, 0x00004bab)</Identifier>
<Source>
<BoxIdentifier>(0x000047dd, 0x00002575)</BoxIdentifier>
<BoxOutputIndex>0</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x00005ae2, 0x000004fd)</BoxIdentifier>
<BoxIdentifier>(0x00002e3e, 0x00002059)</BoxIdentifier>
<BoxInputIndex>0</BoxInputIndex>
</Target>
<Attributes>
......@@ -455,14 +451,14 @@
</Attributes>
</Link>
<Link>
<Identifier>(0x00005f85, 0x00005a35)</Identifier>
<Identifier>(0x000027e0, 0x00000a8f)</Identifier>
<Source>
<BoxIdentifier>(0x00002055, 0x00006c8d)</BoxIdentifier>
<BoxIdentifier>(0x000047dd, 0x00002575)</BoxIdentifier>
<BoxOutputIndex>0</BoxOutputIndex>
</Source>
<Target>
<BoxIdentifier>(0x00004e8d, 0x00003806)</BoxIdentifier>
<BoxInputIndex>0</BoxInputIndex>
<BoxIdentifier>(0x0000360b, 0x0000614e)</BoxIdentifier>
<BoxInputIndex>1</BoxInputIndex>
</Target>
<Attributes>
<Attribute>
......@@ -471,15 +467,15 @@
</Attribute>
<Attribute>
<Identifier>(0x358ae8b5, 0x0f8bacd1)</Identifier>
<Value>224</Value>
<Value>448</Value>
</Attribute>
<Attribute>
<Identifier>(0x3f0a3b27, 0x570913d2)</Identifier>
<Value>369</Value>
<Value>283</Value>
</Attribute>
<Attribute>
<Identifier>(0x6267b5c5, 0x676e3e42)</Identifier>
<Value>208</Value>
<Value>352</Value>
</Attribute>
</Attributes>
</Link>
......
/*
* Receives data from two OpenViBE TCPWriter boxes, a signal writer and a stimulus writer.
* Receives data from one or two OpenViBE TCPWriter boxes, a signal writer and a stimulus writer.
*
* The code reads the two corresponding sockets asynchronously. With signal, the header contents
* are printed first. Next, every time a stimulus is received, the code will print out the stimulus
* and how many bytes of signal have been read and discarded the meanwhile. In order not to make this
* code more complicated by mutexes, the signal thread does not try to print anything to cout but just
* discards the data.
* are printed first. If a stimulus is received, the code will print out the stimulus
* and how many bytes of signal have been read the meanwhile. In order not to make this
* code more complicated by mutexes, the signal thread does not try to print anything to cout.
* Instead, it either discards the data or writes it to a file (if name has been given)
*
* This code is meant to be used with the corresponding scenario 'tcpwriter.xml'
*
......@@ -17,6 +17,7 @@
#ifdef TARGET_HAS_Boost
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <boost/array.hpp>
......@@ -31,50 +32,73 @@ using boost::asio::ip::tcp;
*/
class TCPWriterClient {
public:
TCPWriterClient(boost::asio::io_service& ioService, const char* sAddress, const char* sSignalPort, const char* sStimulusPort) :
stimulusSocket(ioService),
signalSocket(ioService),
signalBytesRead(0)
~TCPWriterClient()
{
if(m_dataFile.is_open()) {
m_dataFile.close();
}
}
TCPWriterClient(boost::asio::io_service& ioService, const char* sAddress, const char* sSignalPort, const char* sStimulusPort,
const char* sDataFilename) :
m_stimulusSocket(ioService),
m_signalSocket(ioService),
m_signalBytesRead(0)
{
boost::system::error_code error;
tcp::resolver resolver(ioService);
// Signal port
std::cout << "Connecting to signal port [" << sAddress << " : " << sSignalPort << "]\n";
tcp::resolver::query query = tcp::resolver::query(tcp::v4(), sAddress, sSignalPort);
signalSocket.connect(*resolver.resolve(query), error);
// Stimulus port
std::cout << "Connecting to stimulus port [" << sAddress << " : " << sStimulusPort << "]\n";
query = tcp::resolver::query(tcp::v4(), sAddress, sStimulusPort);
stimulusSocket.connect(*resolver.resolve(query), error);
if(sDataFilename) {
m_dataFile.open(sDataFilename, std::ios::binary|std::ios::trunc|std::ios::out);
}
if(sStimulusPort && strcmp(sStimulusPort,"0")!=0)
{
// Stimulus port
std::cout << "Connecting to stimulus port [" << sAddress << " : " << sStimulusPort << "]\n";
tcp::resolver::query query = tcp::resolver::query(tcp::v4(), sAddress, sStimulusPort);
m_stimulusSocket.connect(*resolver.resolve(query), error);
// Tell ASIO to read a stimulus
boost::asio::async_read_until(m_stimulusSocket, m_stimulusStream, "\n",
boost::bind(&TCPWriterClient::stimulusHandler, this,
boost::asio::placeholders::error));
}
// Tell ASIO to read a stimulus
boost::asio::async_read_until(stimulusSocket, stimulusStream, "\n",
boost::bind(&TCPWriterClient::stimulusHandler, this,
if(sSignalPort && strcmp(sSignalPort,"0")!=0)
{
// Signal port
std::cout << "Connecting to signal port [" << sAddress << " : " << sSignalPort << "]\n";
tcp::resolver::query query = tcp::resolver::query(tcp::v4(), sAddress, sSignalPort);
m_signalSocket.connect(*resolver.resolve(query), error);
boost::asio::ip::tcp::no_delay l_oNoDelay(true);
m_signalSocket.set_option(l_oNoDelay);
// Tell ASIO to read the signal header
boost::asio::async_read(m_signalSocket, boost::asio::buffer(m_signalHeaderBuffer.data(), m_headerSize),
boost::asio::transfer_at_least(m_headerSize),
boost::bind(&TCPWriterClient::signalHeaderHandler, this,
boost::asio::placeholders::error));
// Tell ASIO to read the signal header
boost::asio::async_read(signalSocket, boost::asio::buffer(signalBuffer.data(), bufferSize),
boost::asio::transfer_at_least(headerSize),
boost::bind(&TCPWriterClient::signalHeaderHandler, this,
boost::asio::placeholders::error));
}
}
tcp::socket stimulusSocket;
tcp::socket signalSocket;
tcp::socket m_stimulusSocket;
tcp::socket m_signalSocket;
static const int headerSize = 32; // signal header size
static const int bufferSize = 1024; // signal buffer size
static const int m_headerSize = 32; // signal header size
int m_bufferSize; // signal buffer size
boost::array<char, headerSize> signalHeaderBuffer;
boost::array<char, bufferSize> signalBuffer;
boost::array<char, m_headerSize> m_signalHeaderBuffer;
std::vector<char> m_signalBuffer;
unsigned long long signalBytesRead; // how many bytes of signal read so far
unsigned long long m_signalBytesRead; // how many bytes of signal read so far
boost::asio::streambuf stimulusStream;
boost::asio::streambuf m_stimulusStream;
std::ofstream m_dataFile;
void stimulusHandler(const boost::system::error_code& error)
{
......@@ -84,10 +108,10 @@ public:
throw boost::system::system_error(error); // Some other error.
// std::cout.write(stimulusBuffer.data(), strlen(stimulusBuffer.data()));
std::cout << "(" << signalBytesRead << " bytes of signal read), stimulus: " << &stimulusStream;
std::cout << "(" << m_signalBytesRead << " bytes of signal read), stimulus: " << &m_stimulusStream;
// Tell ASIO to read again
boost::asio::async_read_until(stimulusSocket, stimulusStream, "\n",
boost::asio::async_read_until(m_stimulusSocket, m_stimulusStream, "\n",
boost::bind(&TCPWriterClient::stimulusHandler, this,
boost::asio::placeholders::error));
}
......@@ -100,7 +124,7 @@ public:
std::cout << "Received signal header\n";
const char *buf = signalBuffer.data();
const char *buf = m_signalHeaderBuffer.data();
// the print assumes the stream is little endian
......@@ -110,11 +134,29 @@ public:
std::cout << " Channels: " << *(unsigned int *)&buf[12] << "\n";
std::cout << " nSamples: " << *(unsigned int *)&buf[16] << "\n";
if(m_dataFile.is_open())
{
m_dataFile.write(buf, m_headerSize);
m_dataFile.flush();
}
std::cout << "Will now read signal in background.\n";
std::cout << "Stimulations will be printed when received...\n";
if(m_stimulusSocket.is_open())
{
std::cout << "Stimulations will be printed when received...\n";
}
const int nChannels = *(unsigned int *)&buf[12];
const int nSamples = *(unsigned int *)&buf[16];
// Note that the buffer size should be the size of the chunk, or the read function may not launch the callback
// before the chunk has been filled. This can cause delays with sparse streams like classifier outputs.
m_bufferSize = nSamples*nChannels*sizeof(double);
m_signalBuffer.resize(m_bufferSize);
// Tell ASIO to read the actual signal
boost::asio::async_read(signalSocket, boost::asio::buffer(signalBuffer.data(), bufferSize),
boost::asio::async_read(m_signalSocket, boost::asio::buffer(m_signalBuffer.data(), m_bufferSize),
boost::asio::transfer_at_least(m_bufferSize),
boost::bind(&TCPWriterClient::signalHandler, this,
boost::asio::placeholders::error));
}
......@@ -126,16 +168,23 @@ public:
throw boost::system::system_error(error); // Some other error.
// signalBuffer will now contain bufferSize/sizeof(float64) amount of samples in float64s. Here we just discard the data.
const char *buf = signalBuffer.data();
const char *buf = m_signalBuffer.data();
const double* signalData = reinterpret_cast<const double*>(buf);
// Use this to fill your data matrix with the signal...
(void)signalData;
signalBytesRead += bufferSize;
if(m_dataFile.is_open())
{
m_dataFile.write(buf, m_bufferSize);
m_dataFile.flush();
}
m_signalBytesRead += m_bufferSize;
// Tell ASIO to read more
boost::asio::async_read(signalSocket, boost::asio::buffer(signalBuffer.data(), bufferSize),
boost::asio::async_read(m_signalSocket, boost::asio::buffer(m_signalBuffer.data(), m_bufferSize),
boost::asio::transfer_at_least(m_bufferSize),
boost::bind(&TCPWriterClient::signalHandler, this,
boost::asio::placeholders::error));
}
......@@ -148,27 +197,33 @@ int main(int argc, char** argv)
{
try
{
if(argc>1 && ( (strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0) || argc!=4 ) )
if( argc>1 && ( (strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0) || (argc!=4 && argc!=5 ) ) )
{
std::cout << "Usage: " << argv[0] << " ip_address signal_port stimulus_port" << std::endl;
std::cout << "Usage: " << argv[0] << " ip_address signal_port stimulus_port [signal_file]" << std::endl;
std::cout << "If no argument is given, the program tries to communicate on localhost using default port value." << std::endl;
std::cout << "Ports that are 0 will be skipped. Optional filename can be specified to write the signal to." << std::endl;
return EXIT_SUCCESS;
}
const char* l_sHostname = "localhost";
const char* l_sSignalPort = "5678";
const char* l_sStimulusPort = "5679";
const char* l_sFilename = NULL;
if(argc == 4)
if(argc >= 4)
{
l_sHostname = argv[1]; l_sSignalPort = argv[2]; l_sStimulusPort = argv[3];
}
if(argc == 5)
{
l_sFilename = argv[4];
}
std::cout << "This example is intended to be used together with the tcp-writer.xml tutorial.\n\n";
boost::asio::io_service ioService;
TCPWriterClient client(ioService, l_sHostname, l_sSignalPort, l_sStimulusPort);
TCPWriterClient client(ioService, l_sHostname, l_sSignalPort, l_sStimulusPort, l_sFilename);
ioService.run();
}
......
......@@ -17,10 +17,10 @@ Output:
1b) If the output is chosen as hex string or descriptive string (these are valid for Stimulation input only), no header is sent.
2) After the possible global header, the data itself is sent. The data is a stream of float64 for Signal and StreamedMatrix.
For signals, the data orientation is [nSamples x nChannels], i.e. all channels for one sample are sent in a sequence, then all channels of the next sample,
and so on (the matrix is a transpose of the usual OpenViBE signal orientation). For matrices, the data is sent as is, in row-major order.
For Stimulations, the data is uint64 if the user chooses raw, or char strings otherwise.
2) After the possible global header, the data itself is sent. The data is a stream of float64 chunks for Signal and StreamedMatrix.
Each chunk is a matrix [nChannels x nSamples], sent in row-major order, i.e. all samples for one channel are sent in a sequence (a row),
then all samples of the next sample (next row), and so on. For Stimulations, the data is uint64 if the user chooses raw,
or char strings otherwise.
Multiple clients can connect to the socket of the box. The box keeps sending data to each client until either the scenario is stopped or the client disconnects. The box does not guarantee that the client starts to receive the input stream from any particular location. When kernel calls box::process() at time t, all clients connected at time t get forwarded the chunks given to box::process() at t. However, if a client establish a connection during box::process(), it may get a partial chunk of t and the whole chunk of t+1 and so on.
......
......@@ -87,7 +87,6 @@ boolean CBoxAlgorithmTCPWriter::initialize(void)
else if(m_oInputType == OV_TypeId_Signal)
{
m_pActiveDecoder = &m_oSignalDecoder;
m_oChunkTranspose.setDimensionCount(0);
}
else
{
......@@ -95,7 +94,7 @@ boolean CBoxAlgorithmTCPWriter::initialize(void)
}
m_pActiveDecoder->initialize(*this,0);
uint64 l_ui64Port = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
const uint64 l_ui64Port = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
m_ui64OutputStyle = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
m_ui32RawVersion = htonl(1); // TCP Writer output format version
......@@ -263,38 +262,41 @@ boolean CBoxAlgorithmTCPWriter::process(void)
m_pActiveDecoder->decode(j);
if(m_pActiveDecoder->isHeaderReceived())
{
if(m_pActiveDecoder == &m_oMatrixDecoder)
// Matrix part
if(m_pActiveDecoder == &m_oMatrixDecoder || m_pActiveDecoder == &m_oSignalDecoder)
{
const uint32 l_ui32NumberOfDimensions = static_cast<uint32> (m_oMatrixDecoder.getOutputMatrix()->getDimensionCount() );
if(l_ui32NumberOfDimensions != 2)
// Casting to base class, ok
OpenViBEToolkit::TStreamedMatrixDecoder < CBoxAlgorithmTCPWriter >* l_pDecoder
= (OpenViBEToolkit::TStreamedMatrixDecoder < CBoxAlgorithmTCPWriter >*)m_pActiveDecoder;
const uint32 l_ui32NumberOfDimensions = l_pDecoder->getOutputMatrix()->getDimensionCount();
switch(l_ui32NumberOfDimensions)
{
this->getLogManager() << LogLevel_Error << "TCPWriter only supports 2 dimensional matrices\n";
case 0:
this->getLogManager() << LogLevel_Error << "Nothing to send, zero size matrix stream received\n";
return false;
case 1:
// Ok, this is a vector, openvibe style. Interpret it as 1 channel row vector.
m_ui32NumberOfChannels = 1;
m_ui32NumberOfSamplesPerChunk = l_pDecoder->getOutputMatrix()->getDimensionSize(0);
break;
case 2:
m_ui32NumberOfChannels = l_pDecoder->getOutputMatrix()->getDimensionSize(0);
m_ui32NumberOfSamplesPerChunk = l_pDecoder->getOutputMatrix()->getDimensionSize(1);
break;
default:
this->getLogManager() << LogLevel_Error << "Only 1 and 2 dimensional matrices are supported\n";
return false;
}
m_ui32NumberOfChannels = static_cast<uint32> (m_oMatrixDecoder.getOutputMatrix()->getDimensionSize(0) );
m_ui32NumberOfSamplesPerChunk = static_cast<uint32> (m_oMatrixDecoder.getOutputMatrix()->getDimensionSize(1) );
}
else if(m_pActiveDecoder == &m_oSignalDecoder)
// Signal specific part
if(m_pActiveDecoder == &m_oSignalDecoder)
{
const uint32 l_ui32NumberOfDimensions = static_cast<uint32> (m_oSignalDecoder.getOutputMatrix()->getDimensionCount() );
if(l_ui32NumberOfDimensions != 2)
{
this->getLogManager() << LogLevel_Error << "TCPWriter only supports 2 dimensional matrices\n";
return false;
}
m_ui32NumberOfChannels = static_cast<uint32> ( m_oSignalDecoder.getOutputMatrix()->getDimensionSize(0) );
m_ui32NumberOfSamplesPerChunk = static_cast<uint32> ( m_oSignalDecoder.getOutputMatrix()->getDimensionSize(1) );
m_ui32Frequency = static_cast<uint32> ( m_oSignalDecoder.getOutputSamplingRate() );\
// C++ is in row major order and openvibe signal matrices are [nChannels x nSamples]. The following buffer
// is used for transposing the chunks to get [nSamples x nChannels] output matrix (only used in case of signals).
m_oChunkTranspose.setDimensionCount(2);
m_oChunkTranspose.setDimensionSize(0,m_ui32NumberOfSamplesPerChunk);
m_oChunkTranspose.setDimensionSize(1,m_ui32NumberOfChannels);
m_ui32Frequency = static_cast<uint32> ( m_oSignalDecoder.getOutputSamplingRate() );
}
else
if(m_pActiveDecoder == &m_oStimulationDecoder)
{
// Stimulus, do nothing
}
......@@ -321,22 +323,7 @@ boolean CBoxAlgorithmTCPWriter::process(void)
{
const IMatrix* l_pMatrix = m_oSignalDecoder.getOutputMatrix();
// Transpose
const float64 *l_pSource = l_pMatrix->getBuffer();
float64 *l_pDestination = m_oChunkTranspose.getBuffer();
if(m_oChunkTranspose.getDimensionCount() == 0) {
this->getLogManager() << LogLevel_Debug << "Box received buffer before header. Bad.\n";
}
for(uint32 c=0;c<m_ui32NumberOfChannels;c++)
{
for(uint32 s=0;s<m_ui32NumberOfSamplesPerChunk;s++)
{
l_pDestination[s*m_ui32NumberOfChannels+c] = l_pSource[c*m_ui32NumberOfSamplesPerChunk+s];
}
}
sendToClients((void *)l_pDestination, l_pMatrix->getBufferElementCount()*sizeof(float64));
sendToClients((void *)l_pMatrix->getBuffer(), l_pMatrix->getBufferElementCount()*sizeof(float64));
}
else // stimulus
{
......
......@@ -73,8 +73,6 @@ namespace OpenViBEPlugins
boost::asio::ip::tcp::acceptor* m_pAcceptor;
std::vector<boost::asio::ip::tcp::socket*> m_vSockets;
OpenViBE::CMatrix m_oChunkTranspose;
OpenViBE::uint64 m_ui64OutputStyle;
OpenViBE::CIdentifier m_oInputType;
......@@ -163,7 +161,6 @@ namespace OpenViBEPlugins
rBoxAlgorithmPrototype.addSetting("Port",OV_TypeId_Integer,"5678");
rBoxAlgorithmPrototype.addSetting("Stimulus output", OVP_TypeID_TCPWriter_OutputStyle, "Raw");
rBoxAlgorithmPrototype.addFlag(OpenViBE::Kernel::BoxFlag_CanModifySetting);
rBoxAlgorithmPrototype.addFlag(OpenViBE::Kernel::BoxFlag_CanModifyInput);
rBoxAlgorithmPrototype.addInputSupport(OV_TypeId_StreamedMatrix);
......