/***************************************************/ /*! \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 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; kclipTest( data_[k] ); *ptr++ = (signed char) (data_[k] * 127.0); } } else if ( dataType_ == STK_SINT16 ) { SINT16 *ptr = (SINT16 *) buffer_; for ( unsigned long k=0; kclipTest( 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; kclipTest( 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; kclipTest( 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; kclipTest( 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; jincrementFrame(); } 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; iincrementFrame(); } } } // stk namespace