Files
stk/src/InetWvOut.cpp

232 lines
6.5 KiB
C++

/***************************************************/
/*! \class InetWvOut
\brief STK internet streaming output class.
This WvOut subclass can stream data over a network via a TCP or
UDP socket connection. The data is converted to big-endian byte
order, if necessary, before being transmitted.
InetWvOut supports multi-channel data. It is important to
distinguish the tick() method that outputs a single sample to all
channels in a sample frame from the overloaded one that takes a
reference to an StkFrames object for multi-channel and/or
multi-frame data.
This class connects to a socket server, the port and IP address of
which must be specified as constructor arguments. The default
data type is signed 16-bit integers but any of the defined
StkFormats are permissible.
by Perry R. Cook and Gary P. Scavone, 1995--2017.
*/
/***************************************************/
#include "InetWvOut.h"
#include "TcpClient.h"
#include "UdpSocket.h"
#include <sstream>
namespace stk {
InetWvOut :: InetWvOut( unsigned long packetFrames )
: buffer_(0), soket_(0), bufferFrames_(packetFrames), bufferBytes_(0)
{
}
InetWvOut :: InetWvOut( int port, Socket::ProtocolType protocol, std::string hostname,
unsigned int nChannels, Stk::StkFormat format, unsigned long packetFrames )
: buffer_(0), soket_(0), bufferFrames_(packetFrames), bufferBytes_(0)
{
connect( port, protocol, hostname, nChannels, format );
}
InetWvOut :: ~InetWvOut()
{
disconnect();
if ( soket_ ) delete soket_;
if ( buffer_ ) delete [] buffer_;
}
void InetWvOut :: connect( int port, Socket::ProtocolType protocol, std::string hostname,
unsigned int nChannels, Stk::StkFormat format )
{
if ( soket_ && soket_->isValid( soket_->id() ) )
disconnect();
if ( nChannels == 0 ) {
oStream_ << "InetWvOut::connect: the channel argument must be greater than zero!";
handleError( StkError::FUNCTION_ARGUMENT );
}
if ( format == STK_SINT8 ) dataBytes_ = 1;
else if ( format == STK_SINT16 ) dataBytes_ = 2;
else if ( format == STK_SINT32 || format == STK_FLOAT32 ) dataBytes_ = 4;
else if ( format == STK_FLOAT64 ) dataBytes_ = 8;
else {
oStream_ << "InetWvOut::connect: unknown data type specified.";
handleError( StkError::FUNCTION_ARGUMENT );
}
dataType_ = format;
if ( protocol == Socket::PROTO_TCP ) {
soket_ = new TcpClient( port, hostname );
}
else {
// For UDP sockets, the sending and receiving sockets cannot have
// the same port number. Since the port argument corresponds to
// the destination port, we will associate this socket instance
// with a different port number (arbitrarily determined as port -
// 1).
UdpSocket *socket = new UdpSocket( port - 1 );
socket->setDestination( port, hostname );
soket_ = (Socket *) socket;
}
// Allocate new memory if necessary.
data_.resize( bufferFrames_, nChannels );
unsigned long bufferBytes = dataBytes_ * bufferFrames_ * nChannels;
if ( bufferBytes > bufferBytes_ ) {
if ( buffer_) delete [] buffer_;
buffer_ = (char *) new char[ bufferBytes ];
bufferBytes_ = bufferBytes;
}
frameCounter_ = 0;
bufferIndex_ = 0;
iData_ = 0;
}
void InetWvOut :: disconnect(void)
{
if ( soket_ ) {
writeData( bufferIndex_ );
soket_->close( soket_->id() );
delete soket_;
soket_ = 0;
}
}
void InetWvOut :: writeData( unsigned long frames )
{
unsigned long samples = frames * data_.channels();
if ( dataType_ == STK_SINT8 ) {
signed char *ptr = (signed char *) buffer_;
for ( unsigned long k=0; k<samples; k++ ) {
this->clipTest( data_[k] );
*ptr++ = (signed char) (data_[k] * 127.0);
}
}
else if ( dataType_ == STK_SINT16 ) {
SINT16 *ptr = (SINT16 *) buffer_;
for ( unsigned long k=0; k<samples; k++ ) {
this->clipTest( data_[k] );
*ptr = (SINT16) (data_[k] * 32767.0);
#ifdef __LITTLE_ENDIAN__
swap16 ((unsigned char *)ptr);
#endif
ptr++;
}
}
else if ( dataType_ == STK_SINT32 ) {
SINT32 *ptr = (SINT32 *) buffer_;
for ( unsigned long k=0; k<samples; k++ ) {
this->clipTest( data_[k] );
*ptr = (SINT32) (data_[k] * 2147483647.0);
#ifdef __LITTLE_ENDIAN__
swap32 ((unsigned char *)ptr);
#endif
ptr++;
}
}
else if ( dataType_ == STK_FLOAT32 ) {
FLOAT32 *ptr = (FLOAT32 *) buffer_;
for ( unsigned long k=0; k<samples; k++ ) {
this->clipTest( data_[k] );
*ptr = (FLOAT32) data_[k];
#ifdef __LITTLE_ENDIAN__
swap32 ((unsigned char *)ptr);
#endif
ptr++;
}
}
else if ( dataType_ == STK_FLOAT64 ) {
FLOAT64 *ptr = (FLOAT64 *) buffer_;
for ( unsigned long k=0; k<samples; k++ ) {
this->clipTest( data_[k] );
*ptr = (FLOAT64) data_[k];
#ifdef __LITTLE_ENDIAN__
swap64 ((unsigned char *)ptr);
#endif
ptr++;
}
}
long bytes = dataBytes_ * samples;
if ( soket_->writeBuffer( (const void *)buffer_, bytes, 0 ) < 0 ) {
oStream_ << "InetWvOut: connection to socket server failed!";
handleError( StkError::PROCESS_SOCKET );
}
}
void InetWvOut :: incrementFrame( void )
{
frameCounter_++;
bufferIndex_++;
if ( bufferIndex_ == bufferFrames_ ) {
writeData( bufferFrames_ );
bufferIndex_ = 0;
iData_ = 0;
}
}
void InetWvOut :: tick( const StkFloat sample )
{
if ( !soket_ || !soket_->isValid( soket_->id() ) ) {
#if defined(_STK_DEBUG_)
oStream_ << "InetWvOut::tick(): a valid socket connection does not exist!";
handleError( StkError::DEBUG_PRINT );
#endif
return;
}
unsigned int nChannels = data_.channels();
StkFloat input = sample;
clipTest( input );
for ( unsigned int j=0; j<nChannels; j++ )
data_[iData_++] = input;
this->incrementFrame();
}
void InetWvOut :: tick( const StkFrames& frames )
{
if ( !soket_ || !soket_->isValid( soket_->id() ) ) {
#if defined(_STK_DEBUG_)
oStream_ << "InetWvOut::tick(): a valid socket connection does not exist!";
handleError( StkError::DEBUG_PRINT );
#endif
return;
}
#if defined(_STK_DEBUG_)
if ( data_.channels() != frames.channels() ) {
oStream_ << "InetWvOut::tick(): incompatible channel value in StkFrames argument!";
handleError( StkError::FUNCTION_ARGUMENT );
}
#endif
unsigned int j, nChannels = data_.channels();
unsigned int iFrames = 0;
for ( unsigned int i=0; i<frames.frames(); i++ ) {
for ( j=0; j<nChannels; j++ ) {
data_[iData_] = frames[iFrames++];
clipTest( data_[iData_++] );
}
this->incrementFrame();
}
}
} // stk namespace