ITANetAudioStream.cpp 12.2 KB
Newer Older
Anne Heimes's avatar
Anne Heimes committed
1 2 3 4 5 6 7 8 9 10 11
#include <ITANetAudioStream.h>

#include <ITANetAudioStreamingClient.h>

// ITA includes
#include <ITAException.h>
#include <ITADataLog.h>
#include <ITAStreamInfo.h>
#include <ITAClock.h>
#include <iomanip> 

Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
12 13 14 15
// Vista includes
#include <VistaBase/VistaStreamUtils.h>

// STL includes
Anne Heimes's avatar
Anne Heimes committed
16 17 18 19
#include <cmath>
#include <iostream>

//! Audio streaming log item
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
struct ITAAudioLog : public ITALogDataBase
{
	inline static std::ostream& outputDesc( std::ostream& os )
	{
		os << "\t" << "Channel";
		os << "\t" << "Samplerate";
		os << "\t" << "BufferSize";
		os << "\t" << "RingBufferSize";
		os << "\t" << "TargetSampleLatency";
		os << std::endl;
		return os;
	};

	inline std::ostream& outputData( std::ostream& os ) const
	{
		os << "\t" << iChannel;
		os << "\t" << dSampleRate;
		os << "\t" << iBufferSize;
		os << "\t" << iRingBufferSize;
		os << "\t" << iTargetSampleLatency;
		os << std::endl;
		return os;
	};

	int iChannel;
	double dSampleRate;
	int iBufferSize;
	int iRingBufferSize;
	int iTargetSampleLatency;
};
	//! Audio streaming log item
Anne Heimes's avatar
Anne Heimes committed
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
struct ITAStreamLog : public ITALogDataBase
{
	inline static std::ostream& outputDesc( std::ostream& os )
	{
		os << "BlockId";
		os << "\t" << "WorldTimeStamp";
		os << "\t" << "StreamingTimeCode";
		os << "\t" << "StreamingStatus";
		os << "\t" << "FreeSamples";
		os << std::endl;
		return os;
	};

	inline std::ostream& outputData( std::ostream& os ) const
	{
		os << uiBlockId;
		os << "\t" << std::setprecision( 12 ) << dWorldTimeStamp;
		os << "\t" << std::setprecision( 12 ) << dStreamingTimeCode;
		os << "\t" << iStreamingStatus;
		os << "\t" << iFreeSamples;
		os << std::endl;
		return os;
	};

	unsigned int uiBlockId; //!< Block identifier (audio streaming)
	double dWorldTimeStamp;
	double dStreamingTimeCode;
	int iStreamingStatus; //!< ... usw
	int iFreeSamples;

};

//! Network streaming log item
struct ITANetLog : public ITALogDataBase
{
	inline static std::ostream& outputDesc( std::ostream& os )
	{
		os << "BlockId";
		os << "\t" << "WorldTimeStamp";
		os << "\t" << "Bufferstatus";
		os << "\t" << "FreeSamples";
		os << "\t" << "NumSamplesTransmitted";
		os << std::endl;
		return os;
	};
	
	inline std::ostream& outputData( std::ostream& os ) const
	{
		os << uiBlockId;
		os << "\t" << std::setprecision( 12 ) << dWorldTimeStamp;
		os << "\t" << iBufferStatus;
		os << "\t" << iFreeSamples;
		os << "\t" << iNumSamplesTransmitted;
		os << std::endl;
		return os;
	};

	unsigned int uiBlockId;
	double dWorldTimeStamp;
	int iBufferStatus;
	int iFreeSamples;
	int iNumSamplesTransmitted;
};

115
class ITABufferedDataLoggerImplAudio : public ITABufferedDataLogger < ITAAudioLog > {};
Anne Heimes's avatar
Anne Heimes committed
116 117 118 119 120 121 122 123 124 125 126
class ITABufferedDataLoggerImplStream : public ITABufferedDataLogger < ITAStreamLog > {};
class ITABufferedDataLoggerImplNet : public ITABufferedDataLogger < ITANetLog > {};


CITANetAudioStream::CITANetAudioStream( int iChannels, double dSamplingRate, int iBufferSize, int iRingBufferCapacity )
	: m_sfOutputStreamBuffer( iChannels, iBufferSize, true )
	, m_dSampleRate( dSamplingRate )
	, m_sfRingBuffer( iChannels, iRingBufferCapacity, true )
	, m_bRingBufferFull( false )
	, m_iStreamingStatus( INVALID )
	, m_dLastStreamingTimeCode( 0.0f )
127
	, m_iTargetSampleLatencyServer( iRingBufferCapacity )
Anne Heimes's avatar
Anne Heimes committed
128 129 130 131 132 133 134 135 136
{
	m_bRingBufferFull = false;
	if( iBufferSize > iRingBufferCapacity )
		ITA_EXCEPT1( INVALID_PARAMETER, "Ring buffer capacity can not be smaller than buffer size." );

	m_pNetAudioStreamingClient = new CITANetAudioStreamingClient( this );
	m_iReadCursor = 0;
	m_iWriteCursor = 0; // always ahead, i.e. iWriteCursor >= iReadCursor if unwrapped

137
	m_iStreamingStatus = STOPPED;
138

139
	// Logging
Anne Heimes's avatar
Anne Heimes committed
140
	std::string paras = std::string("NetAudioLogBaseData") + std::string("_BS") + std::to_string(iBufferSize) + std::string("_Ch") + std::to_string(iChannels) + std::string(".txt");
141
	m_pAudioLogger = new ITABufferedDataLoggerImplAudio( );
Anne Heimes's avatar
Anne Heimes committed
142
	m_pAudioLogger->setOutputFile(paras);
143

Anne Heimes's avatar
Anne Heimes committed
144
	paras = std::string("NetAudioLogStream") + std::string("_BS") + std::to_string(iBufferSize) + std::string("_Ch") + std::to_string(iChannels) + std::string(".txt");
Anne Heimes's avatar
Anne Heimes committed
145
	m_pStreamLogger = new ITABufferedDataLoggerImplStream();
Anne Heimes's avatar
Anne Heimes committed
146
	m_pStreamLogger->setOutputFile(paras);
Anne Heimes's avatar
Anne Heimes committed
147 148
	iAudioStreamingBlockID = 0;

Anne Heimes's avatar
Anne Heimes committed
149
	paras = std::string("NetAudioLogNet") + std::string("_BS") + std::to_string(iBufferSize) + std::string("_Ch") + std::to_string(iChannels) + std::string(".txt");
Anne Heimes's avatar
Anne Heimes committed
150
	m_pNetLogger = new ITABufferedDataLoggerImplNet();
Anne Heimes's avatar
Anne Heimes committed
151
	m_pNetLogger->setOutputFile(paras);
Anne Heimes's avatar
Anne Heimes committed
152
	iNetStreamingBlockID = 0;
153 154 155 156 157 158 159

	// Logging Base Data
	ITAAudioLog oLog;
	oLog.iChannel = GetNumberOfChannels();
	oLog.dSampleRate = m_dSampleRate;
	oLog.iBufferSize = GetBlocklength();
	oLog.iRingBufferSize = GetRingBufferSize();
160
	oLog.iTargetSampleLatency = m_iTargetSampleLatencyServer;
161
	m_pAudioLogger->log( oLog );
Anne Heimes's avatar
Anne Heimes committed
162 163 164 165 166
}

CITANetAudioStream::~CITANetAudioStream()
{
	delete m_pNetLogger;
Anne's avatar
Anne committed
167
	delete m_pStreamLogger;
168
	delete m_pAudioLogger;
Anne's avatar
Anne committed
169
	delete m_pNetAudioStreamingClient;
Anne Heimes's avatar
Anne Heimes committed
170 171 172 173 174 175 176
}

bool CITANetAudioStream::Connect( const std::string& sAddress, int iPort )
{
	return m_pNetAudioStreamingClient->Connect( sAddress, iPort );
}

177 178 179 180 181
void CITANetAudioStream::Disconnect()
{
	m_pNetAudioStreamingClient->Disconnect();
}

Anne Heimes's avatar
Anne Heimes committed
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
bool CITANetAudioStream::GetIsConnected() const
{
	return m_pNetAudioStreamingClient->GetIsConnected();
}

void CITANetAudioStream::SetAllowedLatencySeconds( float fLatencySeconds )
{
	SetAllowedLatencySamples( (int) std::floor( GetSampleRate() * fLatencySeconds ) );
}

float CITANetAudioStream::GetMaximumLatencySeconds() const
{
	return float( GetMaximumLatencySamples() / GetSampleRate() );
}

float CITANetAudioStream::GetMinimumLatencySeconds() const
{
	return float( GetMinimumLatencySamples() / GetSampleRate() );
}

void CITANetAudioStream::SetAllowedLatencySamples( int iLatencySamples )
{
	if( iLatencySamples < GetMinimumLatencySamples() )
		ITA_EXCEPT1( INVALID_PARAMETER, "Can not set latency lower than the minimum possible" );

	if( iLatencySamples > GetMaximumLatencySamples() )
		ITA_EXCEPT1( INVALID_PARAMETER, "Can not set latency greater than the maximum possible" );

210
	m_iTargetSampleLatencyServer = iLatencySamples;
Anne Heimes's avatar
Anne Heimes committed
211 212 213 214
}

float CITANetAudioStream::GetAllowedLatencySeconds() const
{
215
	return float(m_iTargetSampleLatencyServer / GetSampleRate());
Anne Heimes's avatar
Anne Heimes committed
216 217 218 219
}

int CITANetAudioStream::GetAllowedLatencySamples() const
{
220
	return m_iTargetSampleLatencyServer;
Anne Heimes's avatar
Anne Heimes committed
221 222 223 224 225 226 227 228 229 230 231 232 233
}

int CITANetAudioStream::GetMinimumLatencySamples() const
{
	// At least one block
	return GetBlocklength();
}

int CITANetAudioStream::GetMaximumLatencySamples() const
{
	return GetRingBufferSize();
}

234 235 236 237 238
void CITANetAudioStream::SetLatencyForRealtime()
{
	SetAllowedLatencySamples( GetMinimumLatencySamples() );
}

Anne Heimes's avatar
Anne Heimes committed
239 240 241 242 243
const float* CITANetAudioStream::GetBlockPointer( unsigned int uiChannel, const ITAStreamInfo* pInfo )
{
	if( !GetIsConnected() )
	{
		m_sfOutputStreamBuffer[ uiChannel ].Zero( );
Anne's avatar
Anne committed
244 245
		if (uiChannel == 0 )
			m_iStreamingStatus = STOPPED;
Anne Heimes's avatar
Anne Heimes committed
246 247 248 249 250 251
	}
	else
	{
		if( GetIsRingBufferEmpty() )
		{
			m_sfOutputStreamBuffer[ uiChannel ].Zero();
Anne's avatar
Anne committed
252 253
			if (uiChannel == 0 )
				m_iStreamingStatus = BUFFER_UNDERRUN;
Anne's avatar
Anne committed
254
#if NET_AUDIO_SHOW_TRAFFIC
255
			//vstr::out() << "[ Stream ] Buffer underrun" << std::endl;
Anne's avatar
Anne committed
256
#endif
Anne Heimes's avatar
Anne Heimes committed
257 258 259 260 261 262 263
		}
		else
		{
			if( GetRingBufferAvailableSamples() < int( GetBlocklength() ) )
			{
				// @todo: fade out
				m_sfRingBuffer[ uiChannel ].Zero();
Anne's avatar
Anne committed
264 265
				if (uiChannel == 0 )
					m_iStreamingStatus = BUFFER_UNDERRUN;
Anne's avatar
Anne committed
266
#if NET_AUDIO_SHOW_TRAFFIC
267
				//vstr::out() << "[ Stream ] Buffer underrun" << std::endl;
Anne's avatar
Anne committed
268
#endif
Anne Heimes's avatar
Anne Heimes committed
269 270 271 272 273
			}
			else
			{
				// Normal behaviour (if everything is OK with ring buffer status)
				m_sfRingBuffer[ uiChannel ].cyclic_read( m_sfOutputStreamBuffer[ uiChannel ].GetData(), GetBlocklength(), m_iReadCursor );
Anne's avatar
Anne committed
274 275
				if ( uiChannel == 0 )
					m_iStreamingStatus = STREAMING;
Anne's avatar
Anne committed
276
#if NET_AUDIO_SHOW_TRAFFIC
Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
277
				vstr::out() << "[ Stream ] Streaming" << std::endl;
Anne's avatar
Anne committed
278
#endif
Anne Heimes's avatar
Anne Heimes committed
279 280 281 282 283 284 285
			}
		}		
	}

	if( uiChannel == 0 )
		m_dLastStreamingTimeCode = pInfo->dTimecode;

Anne's avatar
Anne committed
286

Anne Heimes's avatar
Anne Heimes committed
287 288 289 290 291 292 293
	return m_sfOutputStreamBuffer[uiChannel].GetData();
}

void CITANetAudioStream::IncrementBlockPointer()
{
	// Increment read cursor by one audio block and wrap around if exceeding ring buffer
	int iSavedSample = GetRingBufferSize( ) - GetRingBufferFreeSamples( );
294 295
	if ( !GetIsConnected( ) )
	{
Anne's avatar
Anne committed
296
		//m_iStreamingStatus = STOPPED;
297
	} else if ( iSavedSample >= int( GetBlocklength( ) ) )
Anne Heimes's avatar
Anne Heimes committed
298 299 300
	{
		//es wurden Samples abgespielt
		m_iReadCursor = ( m_iReadCursor + m_sfOutputStreamBuffer.GetLength() ) % m_sfRingBuffer.GetLength();
Anne's avatar
Anne committed
301
		//m_iStreamingStatus = STREAMING;
Anne's avatar
Anne committed
302
#if NET_AUDIO_SHOW_TRAFFIC
303
		//vstr::out() << "[ Stream ] Streaming" << std::endl;
Anne's avatar
Anne committed
304
#endif
Anne Heimes's avatar
Anne Heimes committed
305 306 307
	}
	else if ( GetIsRingBufferEmpty( ) )
	{
Anne's avatar
Anne committed
308
		//m_iStreamingStatus = BUFFER_UNDERRUN;
Anne's avatar
Anne committed
309
#if NET_AUDIO_SHOW_TRAFFIC
310
		//vstr::out() << "[ Stream ] Buffer underrun" << std::endl;
Anne's avatar
Anne committed
311
#endif
Anne Heimes's avatar
Anne Heimes committed
312 313 314
	}
	else
	{
Anne's avatar
Anne committed
315
		//m_iStreamingStatus = BUFFER_UNDERRUN;
Anne's avatar
Anne committed
316
#if NET_AUDIO_SHOW_TRAFFIC
317
		//vstr::out() << "[ Stream ] Buffer underrun" << std::endl;
Anne's avatar
Anne committed
318
#endif
Anne Heimes's avatar
Anne Heimes committed
319 320 321 322
		m_iReadCursor = m_iWriteCursor;
	}
	m_bRingBufferFull = false;

Anne's avatar
Anne committed
323
	ITAStreamLog oLog;	
Anne Heimes's avatar
Anne Heimes committed
324 325 326 327
	oLog.iStreamingStatus = m_iStreamingStatus;
	oLog.dWorldTimeStamp = ITAClock::getDefaultClock()->getTime();
	oLog.dStreamingTimeCode = m_dLastStreamingTimeCode;
	oLog.uiBlockId = ++iAudioStreamingBlockID;
328
	oLog.iFreeSamples = GetRingBufferFreeSamples( );
Jonas Stienen's avatar
Jonas Stienen committed
329
	m_pStreamLogger->log( oLog );
Anne Heimes's avatar
Anne Heimes committed
330
	
Anne Heimes's avatar
Anne Heimes committed
331
	//m_pNetAudioStreamingClient->TriggerBlockIncrement();
Anne Heimes's avatar
Anne Heimes committed
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
}

int CITANetAudioStream::Transmit( const ITASampleFrame& sfNewSamples, int iNumSamples )
{
	// Take local copies (concurrent access)
	int iCurrentReadCursor = m_iReadCursor;
	int iCurrentWriteCursor = m_iWriteCursor;

	ITANetLog oLog;
	if( iCurrentWriteCursor < iCurrentReadCursor )
		iCurrentWriteCursor += GetRingBufferSize(); // Unwrap, because write cursor always ahead

	if ( ( m_iWriteCursor == m_iReadCursor ) && m_bRingBufferFull )
	{
		// BufferFull
347
		m_iStreamingStatus = BUFFER_OVERRUN;
Anne's avatar
Anne committed
348
#if NET_AUDIO_SHOW_TRAFFIC
Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
349
		vstr::out() << "[ NetAudio ] Buffer overrun" << std::endl;
Anne's avatar
Anne committed
350
#endif
Anne Heimes's avatar
Anne Heimes committed
351 352 353 354 355
	}
	else if( GetRingBufferFreeSamples() < iNumSamples )
	{
		// @todo: only partly write
		//std::cerr << "BUFFER_OVERRUN! Would partly write samples because ring buffer will be full then." << std::endl;
356 357

		m_iStreamingStatus = BUFFER_OVERRUN;
Anne Heimes's avatar
Anne Heimes committed
358 359 360 361 362 363 364
		m_iWriteCursor = m_iReadCursor;
	}
	else
	{
		// write samples into ring buffer
		m_sfRingBuffer.cyclic_write( sfNewSamples, iNumSamples, 0, iCurrentWriteCursor );
		m_bRingBufferFull = false;
365
		m_iStreamingStatus = STREAMING;
Anne's avatar
Anne committed
366
#if NET_AUDIO_SHOW_TRAFFIC
Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
367
		vstr::out() << "[ NetAudio ] Buffer write" << std::endl;
Anne's avatar
Anne committed
368
#endif
Anne Heimes's avatar
Anne Heimes committed
369 370 371 372 373 374

		// set write curser
		m_iWriteCursor = ( m_iWriteCursor + iNumSamples ) % GetRingBufferSize( );
		if ( m_iWriteCursor == m_iReadCursor )
		{
			m_bRingBufferFull = true;
Anne's avatar
Anne committed
375
#if NET_AUDIO_SHOW_TRAFFIC
Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
376
			vstr::out() << "[ NetAudio ] Buffer overrun" << std::endl;
Anne's avatar
Anne committed
377
#endif
Anne Heimes's avatar
Anne Heimes committed
378 379
		}
	}
380
	oLog.iBufferStatus = m_iStreamingStatus;
Anne Heimes's avatar
Anne Heimes committed
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
	oLog.dWorldTimeStamp = ITAClock::getDefaultClock( )->getTime( );
	oLog.uiBlockId = ++iAudioStreamingBlockID;
	oLog.iFreeSamples = GetRingBufferFreeSamples( );
	oLog.iNumSamplesTransmitted = iNumSamples;
	m_pNetLogger->log( oLog );
	
	return GetRingBufferFreeSamples();
}

int CITANetAudioStream::GetRingBufferAvailableSamples() const
{
	return GetRingBufferSize() - GetRingBufferFreeSamples();
}

int CITANetAudioStream::GetRingBufferFreeSamples() const
{
	if( m_bRingBufferFull )
		return 0;

	int iFreeSamples = GetRingBufferSize() - ( ( m_iWriteCursor - m_iReadCursor + GetRingBufferSize() ) % GetRingBufferSize() );
	assert( iFreeSamples > 0 );
	return iFreeSamples;
}

int CITANetAudioStream::GetRingBufferSize() const
{
	return m_sfRingBuffer.GetLength();
}

bool CITANetAudioStream::GetIsRingBufferFull() const
{
	return m_bRingBufferFull;
}

bool CITANetAudioStream::GetIsRingBufferEmpty() const
{
	return ( !m_bRingBufferFull && m_iReadCursor == m_iWriteCursor );
}

unsigned int CITANetAudioStream::GetBlocklength() const
{
	return  ( unsigned int ) m_sfOutputStreamBuffer.GetLength();
}

unsigned int CITANetAudioStream::GetNumberOfChannels() const
{
	return ( unsigned int ) m_sfOutputStreamBuffer.channels();
}

double CITANetAudioStream::GetSampleRate() const
{
	return m_dSampleRate;
}