📄 inetwvin.cpp
字号:
/***************************************************//*! \class InetWvIn \brief STK internet streaming input class. This Wvin subclass reads streamed audio data over a network via a TCP or UDP socket connection. The data is assumed in big-endian, or network, byte order. Only a single socket connection is supported. InetWvIn supports multi-channel data. It is important to distinguish the tick() methods, which return samples produced by averaging across sample frames, from the tickFrame() methods, which return references or pointers to multi-channel sample frames. This class implements a socket server. When using the TCP protocol, the server "listens" for a single remote connection within the InetWvIn::start() function. For the UDP protocol, no attempt is made to verify packet delivery or order. The default data type for the incoming stream is signed 16-bit integers, though any of the defined StkFormats are permissible. by Perry R. Cook and Gary P. Scavone, 1995 - 2005.*//***************************************************/#include "InetWvIn.h"#if !defined(SYMBIAN)extern "C" THREAD_RETURN THREAD_TYPE inputThread( void * ptr ){ ThreadInfo *info = (ThreadInfo *)ptr; while ( !info->finished ) { ((InetWvIn *) info->object)->receive(); } return 0;}InetWvIn :: InetWvIn( unsigned long bufferFrames, unsigned int nBuffers ) :soket_(0), buffer_(0), bufferFrames_(bufferFrames), bufferBytes_(0), nBuffers_(nBuffers), connected_(false){ threadInfo_.finished = false; threadInfo_.object = (void *) this; // Start the input thread. if ( !thread_.start( &inputThread, &threadInfo_ ) ) { errorString_ << "InetWvIn(): unable to start input thread in constructor!"; handleError( StkError::PROCESS_THREAD ); }}InetWvIn :: ~InetWvIn(){ // Close down the thread. connected_ = false; threadInfo_.finished = true; if ( soket_ ) delete soket_; if ( buffer_ ) delete [] buffer_;}void InetWvIn :: listen( int port, unsigned int nChannels, Stk::StkFormat format, Socket::ProtocolType protocol ){ mutex_.lock(); if ( connected_ ) delete soket_; if ( nChannels < 1 ) { errorString_ << "InetWvIn()::listen(): the channel argument (" << nChannels << ") must be greater than zero."; handleError( StkError::FUNCTION_ARGUMENT ); } if ( format == STK_SINT16 ) dataBytes_ = 2; else if ( format == STK_SINT32 || format == STK_FLOAT32 ) dataBytes_ = 4; else if ( format == STK_FLOAT64 ) dataBytes_ = 8; else if ( format == STK_SINT8 ) dataBytes_ = 1; else { errorString_ << "InetWvIn(): unknown data type specified (" << format << ")."; handleError( StkError::FUNCTION_ARGUMENT ); } dataType_ = format; unsigned long bufferBytes = bufferFrames_ * nBuffers_ * nChannels * dataBytes_; if ( bufferBytes > bufferBytes_ ) { if ( buffer_) delete [] buffer_; buffer_ = (char *) new char[ bufferBytes ]; bufferBytes_ = bufferBytes; } data_.resize( bufferFrames_, nChannels ); lastOutputs_.resize( 1, nChannels, 0.0 ); bufferCounter_ = 0; writePoint_ = 0; readPoint_ = 0; bytesFilled_ = 0; if ( protocol == Socket::PROTO_TCP ) { TcpServer *socket = new TcpServer( port ); errorString_ << "InetWvIn:listen(): waiting for TCP connection on port " << socket->port() << " ... "; handleError( StkError::STATUS ); fd_ = socket->accept(); if ( fd_ < 0) { errorString_ << "InetWvIn::listen(): Error accepting TCP connection request!"; handleError( StkError::PROCESS_SOCKET ); } errorString_ << "InetWvIn::listen(): TCP socket connection made!"; handleError( StkError::STATUS ); soket_ = (Socket *) socket; } else { soket_ = new UdpSocket( port ); fd_ = soket_->id(); } connected_ = true; mutex_.unlock();}void InetWvIn :: receive( void ){ if ( !connected_ ) { Stk::sleep(100); return; } fd_set mask; FD_ZERO( &mask ); FD_SET( fd_, &mask ); // The select function will block until data is available for reading. select( fd_+1, &mask, (fd_set *)0, (fd_set *)0, NULL ); if ( FD_ISSET( fd_, &mask ) ) { mutex_.lock(); unsigned long unfilled = bufferBytes_ - bytesFilled_; if ( unfilled > 0 ) { // There's room in our buffer for more data. unsigned long endPoint = writePoint_ + unfilled; if ( endPoint > bufferBytes_ ) unfilled -= endPoint - bufferBytes_; int i = soket_->readBuffer( fd_, (void *)&buffer_[writePoint_], unfilled, 0 ); //int i = Socket::readBuffer( fd_, (void *)&buffer_[writePoint_], unfilled, 0 ); if ( i <= 0 ) { errorString_ << "InetWvIn::receive(): the remote InetWvIn socket has closed."; handleError( StkError::STATUS ); connected_ = false; mutex_.unlock(); return; } bytesFilled_ += i; writePoint_ += i; if ( writePoint_ == bufferBytes_ ) writePoint_ = 0; mutex_.unlock(); } else { // Sleep 10 milliseconds AFTER unlocking mutex. mutex_.unlock(); Stk::sleep( 10 ); } }}int InetWvIn :: readData( void ){ // We have two potential courses of action should this method // be called and the input buffer isn't sufficiently filled. // One solution is to fill the data buffer with zeros and return. // The other solution is to wait until the necessary data exists. // I chose the latter, as it works for both streamed files // (non-realtime data transport) and realtime playback (given // adequate network bandwidth and speed). // Wait until data is ready. unsigned long bytes = data_.size() * dataBytes_; while ( connected_ && bytesFilled_ < bytes ) Stk::sleep( 10 ); if ( !connected_ && bytesFilled_ == 0 ) return 0; bytes = ( bytesFilled_ < bytes ) ? bytesFilled_ : bytes; // Copy samples from buffer to data. StkFloat gain; long samples = bytes / dataBytes_; mutex_.lock(); if ( dataType_ == STK_SINT16 ) { gain = 1.0 / 32767.0; SINT16 *buf = (SINT16 *) (buffer_+readPoint_); for (int i=0; i<samples; i++ ) {#ifdef __LITTLE_ENDIAN__ swap16((unsigned char *) buf);#endif data_[i] = (StkFloat) *buf++; data_[i] *= gain; } } else if ( dataType_ == STK_SINT32 ) { gain = 1.0 / 2147483647.0; SINT32 *buf = (SINT32 *) (buffer_+readPoint_); for (int i=0; i<samples; i++ ) {#ifdef __LITTLE_ENDIAN__ swap32((unsigned char *) buf);#endif data_[i] = (StkFloat) *buf++; data_[i] *= gain; } } else if ( dataType_ == STK_FLOAT32 ) { FLOAT32 *buf = (FLOAT32 *) (buffer_+readPoint_); for (int i=0; i<samples; i++ ) {#ifdef __LITTLE_ENDIAN__ swap32((unsigned char *) buf);#endif data_[i] = (StkFloat) *buf++; } } else if ( dataType_ == STK_FLOAT64 ) { FLOAT64 *buf = (FLOAT64 *) (buffer_+readPoint_); for (int i=0; i<samples; i++ ) {#ifdef __LITTLE_ENDIAN__ swap64((unsigned char *) buf);#endif data_[i] = (StkFloat) *buf++; } } else if ( dataType_ == STK_SINT8 ) { gain = 1.0 / 127.0; signed char *buf = (signed char *) (buffer_+readPoint_); for (int i=0; i<samples; i++ ) { data_[i] = (StkFloat) *buf++; data_[i] *= gain; } } readPoint_ += bytes; if ( readPoint_ == bufferBytes_ ) readPoint_ = 0; bytesFilled_ -= bytes; if ( bytesFilled_ < 0 ) bytesFilled_ = 0; mutex_.unlock(); return samples / data_.channels();}bool InetWvIn :: isConnected( void ){ if ( bytesFilled_ > 0 || bufferCounter_ > 0 ) return true; else return connected_;}void InetWvIn :: computeFrame( void ){ // If no connection and we've output all samples in the queue, return. if ( !connected_ && bytesFilled_ == 0 && bufferCounter_ == 0 ) return; if ( bufferCounter_ == 0 ) bufferCounter_ = readData(); unsigned int nChannels = lastOutputs_.channels(); long temp = (bufferFrames_ - bufferCounter_) * nChannels; for ( unsigned int i=0; i<nChannels; i++ ) lastOutputs_[i] = data_[temp++]; bufferCounter_--; if ( bufferCounter_ < 0 ) bufferCounter_ = 0; return;}#endif // SYMBIAN
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -