ITANetAudioStream.cpp 10.7 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
#include <cmath>
#include <iostream>

19

Jonas Stienen's avatar
Jonas Stienen committed
20 21
//! Audio streaming log item
struct ITAAudioStreamLog : public ITALogDataBase
Anne Heimes's avatar
Anne Heimes committed
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
{
	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;
39
		os << "\t" << sStreamingStatus;
Anne Heimes's avatar
Anne Heimes committed
40 41 42 43 44 45 46 47
		os << "\t" << iFreeSamples;
		os << std::endl;
		return os;
	};

	unsigned int uiBlockId; //!< Block identifier (audio streaming)
	double dWorldTimeStamp;
	double dStreamingTimeCode;
48
	std::string sStreamingStatus;
Anne Heimes's avatar
Anne Heimes committed
49 50 51 52 53
	int iFreeSamples;

};

//! Network streaming log item
Jonas Stienen's avatar
Jonas Stienen committed
54
struct ITANetworkStreamLog : public ITALogDataBase
Anne Heimes's avatar
Anne Heimes committed
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
{
	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;
};

Jonas Stienen's avatar
Jonas Stienen committed
85 86
class ITABufferedDataLoggerImplStream : public ITABufferedDataLogger < ITAAudioStreamLog > {};
class ITABufferedDataLoggerImplNet : public ITABufferedDataLogger < ITANetworkStreamLog > {};
Anne Heimes's avatar
Anne Heimes committed
87 88


89
CITANetAudioStream::CITANetAudioStream(int iChannels, double dSamplingRate, int iBufferSize, int iTargetSampleLatencyServer)
Anne Heimes's avatar
Anne Heimes committed
90 91
	: m_sfOutputStreamBuffer( iChannels, iBufferSize, true )
	, m_dSampleRate( dSamplingRate )
92
	, m_sfRingBuffer(iChannels, iTargetSampleLatencyServer * 3, true)
Anne Heimes's avatar
Anne Heimes committed
93 94 95 96 97
	, m_bRingBufferFull( false )
	, m_iStreamingStatus( INVALID )
	, m_dLastStreamingTimeCode( 0.0f )
{
	m_bRingBufferFull = false;
98 99
	if (iBufferSize > iTargetSampleLatencyServer)
		ITA_EXCEPT1( INVALID_PARAMETER, "Ring buffer capacity can not be smaller than Target Sample Latency." );
Anne Heimes's avatar
Anne Heimes committed
100 101 102 103 104

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

105
	m_iStreamingStatus = STOPPED;
106

107
	// Logging
Jonas Stienen's avatar
Jonas Stienen committed
108
	m_pAudioStreamLogger = new ITABufferedDataLoggerImplStream();
Anne Heimes's avatar
Anne Heimes committed
109 110
	iAudioStreamingBlockID = 0;

Jonas Stienen's avatar
Jonas Stienen committed
111
	m_pNetworkStreamLogger = new ITABufferedDataLoggerImplNet();
Anne Heimes's avatar
Anne Heimes committed
112
	iNetStreamingBlockID = 0;
113

Jonas Stienen's avatar
Jonas Stienen committed
114
	SetNetAudioStreamingLoggerBaseName( "ITANetAudioStream" );
Anne Heimes's avatar
Anne Heimes committed
115 116 117 118
}

CITANetAudioStream::~CITANetAudioStream()
{
Jonas Stienen's avatar
Jonas Stienen committed
119 120
	delete m_pAudioStreamLogger;
	delete m_pNetworkStreamLogger;
Anne's avatar
Anne committed
121
	delete m_pNetAudioStreamingClient;
Anne Heimes's avatar
Anne Heimes committed
122 123 124 125 126 127 128
}

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

129 130 131 132 133
void CITANetAudioStream::Disconnect()
{
	m_pNetAudioStreamingClient->Disconnect();
}

Anne Heimes's avatar
Anne Heimes committed
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
bool CITANetAudioStream::GetIsConnected() const
{
	return m_pNetAudioStreamingClient->GetIsConnected();
}

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

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

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

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

const float* CITANetAudioStream::GetBlockPointer( unsigned int uiChannel, const ITAStreamInfo* pInfo )
{
	if( !GetIsConnected() )
	{
		m_sfOutputStreamBuffer[ uiChannel ].Zero( );
Anne's avatar
Anne committed
165 166
		if (uiChannel == 0 )
			m_iStreamingStatus = STOPPED;
Anne Heimes's avatar
Anne Heimes committed
167 168 169 170 171 172
	}
	else
	{
		if( GetIsRingBufferEmpty() )
		{
			m_sfOutputStreamBuffer[ uiChannel ].Zero();
Anne's avatar
Anne committed
173 174
			if (uiChannel == 0 )
				m_iStreamingStatus = BUFFER_UNDERRUN;
Anne's avatar
Anne committed
175
#if NET_AUDIO_SHOW_TRAFFIC
176
			//vstr::out() << "[ Stream ] Buffer underrun" << std::endl;
Anne's avatar
Anne committed
177
#endif
Anne Heimes's avatar
Anne Heimes committed
178 179 180 181 182 183 184
		}
		else
		{
			if( GetRingBufferAvailableSamples() < int( GetBlocklength() ) )
			{
				// @todo: fade out
				m_sfRingBuffer[ uiChannel ].Zero();
Anne's avatar
Anne committed
185 186
				if (uiChannel == 0 )
					m_iStreamingStatus = BUFFER_UNDERRUN;
Anne's avatar
Anne committed
187
#if NET_AUDIO_SHOW_TRAFFIC
188
				//vstr::out() << "[ Stream ] Buffer underrun" << std::endl;
Anne's avatar
Anne committed
189
#endif
Anne Heimes's avatar
Anne Heimes committed
190 191 192 193 194
			}
			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
195 196
				if ( uiChannel == 0 )
					m_iStreamingStatus = STREAMING;
Anne's avatar
Anne committed
197
#if NET_AUDIO_SHOW_TRAFFIC
Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
198
				vstr::out() << "[ Stream ] Streaming" << std::endl;
Anne's avatar
Anne committed
199
#endif
Anne Heimes's avatar
Anne Heimes committed
200 201 202 203 204 205 206
			}
		}		
	}

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

Anne's avatar
Anne committed
207

Anne Heimes's avatar
Anne Heimes committed
208 209 210 211 212 213 214
	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( );
215 216
	if ( !GetIsConnected( ) )
	{
Anne's avatar
Anne committed
217
		//m_iStreamingStatus = STOPPED;
218
	} else if ( iSavedSample >= int( GetBlocklength( ) ) )
Anne Heimes's avatar
Anne Heimes committed
219 220 221
	{
		//es wurden Samples abgespielt
		m_iReadCursor = ( m_iReadCursor + m_sfOutputStreamBuffer.GetLength() ) % m_sfRingBuffer.GetLength();
Anne's avatar
Anne committed
222
		//m_iStreamingStatus = STREAMING;
Anne's avatar
Anne committed
223
#if NET_AUDIO_SHOW_TRAFFIC
224
		//vstr::out() << "[ Stream ] Streaming" << std::endl;
Anne's avatar
Anne committed
225
#endif
Anne Heimes's avatar
Anne Heimes committed
226 227 228
	}
	else if ( GetIsRingBufferEmpty( ) )
	{
Anne's avatar
Anne committed
229
		//m_iStreamingStatus = BUFFER_UNDERRUN;
Anne's avatar
Anne committed
230
#if NET_AUDIO_SHOW_TRAFFIC
231
		//vstr::out() << "[ Stream ] Buffer underrun" << std::endl;
Anne's avatar
Anne committed
232
#endif
Anne Heimes's avatar
Anne Heimes committed
233 234 235
	}
	else
	{
Anne's avatar
Anne committed
236
		//m_iStreamingStatus = BUFFER_UNDERRUN;
Anne's avatar
Anne committed
237
#if NET_AUDIO_SHOW_TRAFFIC
238
		//vstr::out() << "[ Stream ] Buffer underrun" << std::endl;
Anne's avatar
Anne committed
239
#endif
Anne Heimes's avatar
Anne Heimes committed
240 241 242 243
		m_iReadCursor = m_iWriteCursor;
	}
	m_bRingBufferFull = false;

Jonas Stienen's avatar
Jonas Stienen committed
244
	ITAAudioStreamLog oLog;	
245
	oLog.sStreamingStatus = GetStreamingStatusString( m_iStreamingStatus );
Anne Heimes's avatar
Anne Heimes committed
246 247 248
	oLog.dWorldTimeStamp = ITAClock::getDefaultClock()->getTime();
	oLog.dStreamingTimeCode = m_dLastStreamingTimeCode;
	oLog.uiBlockId = ++iAudioStreamingBlockID;
249
	oLog.iFreeSamples = GetRingBufferFreeSamples( );
Jonas Stienen's avatar
Jonas Stienen committed
250
	m_pAudioStreamLogger->log( oLog );
Anne Heimes's avatar
Anne Heimes committed
251
	
Anne Heimes's avatar
Anne Heimes committed
252
	//m_pNetAudioStreamingClient->TriggerBlockIncrement();
Anne Heimes's avatar
Anne Heimes committed
253 254 255 256 257 258 259 260
}

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

Jonas Stienen's avatar
Jonas Stienen committed
261
	ITANetworkStreamLog oLog;
Anne Heimes's avatar
Anne Heimes committed
262 263 264 265 266 267
	if( iCurrentWriteCursor < iCurrentReadCursor )
		iCurrentWriteCursor += GetRingBufferSize(); // Unwrap, because write cursor always ahead

	if ( ( m_iWriteCursor == m_iReadCursor ) && m_bRingBufferFull )
	{
		// BufferFull
268
		m_iStreamingStatus = BUFFER_OVERRUN;
Anne's avatar
Anne committed
269
#if NET_AUDIO_SHOW_TRAFFIC
Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
270
		vstr::out() << "[ NetAudio ] Buffer overrun" << std::endl;
Anne's avatar
Anne committed
271
#endif
Anne Heimes's avatar
Anne Heimes committed
272 273 274 275 276
	}
	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;
277 278

		m_iStreamingStatus = BUFFER_OVERRUN;
Anne Heimes's avatar
Anne Heimes committed
279 280 281 282 283 284 285
		m_iWriteCursor = m_iReadCursor;
	}
	else
	{
		// write samples into ring buffer
		m_sfRingBuffer.cyclic_write( sfNewSamples, iNumSamples, 0, iCurrentWriteCursor );
		m_bRingBufferFull = false;
286
		m_iStreamingStatus = STREAMING;
Anne's avatar
Anne committed
287
#if NET_AUDIO_SHOW_TRAFFIC
Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
288
		vstr::out() << "[ NetAudio ] Buffer write" << std::endl;
Anne's avatar
Anne committed
289
#endif
Anne Heimes's avatar
Anne Heimes committed
290 291 292 293 294 295

		// set write curser
		m_iWriteCursor = ( m_iWriteCursor + iNumSamples ) % GetRingBufferSize( );
		if ( m_iWriteCursor == m_iReadCursor )
		{
			m_bRingBufferFull = true;
Anne's avatar
Anne committed
296
#if NET_AUDIO_SHOW_TRAFFIC
Dipl.-Ing. Jonas Stienen's avatar
Dipl.-Ing. Jonas Stienen committed
297
			vstr::out() << "[ NetAudio ] Buffer overrun" << std::endl;
Anne's avatar
Anne committed
298
#endif
Anne Heimes's avatar
Anne Heimes committed
299 300
		}
	}
301
	oLog.iBufferStatus = m_iStreamingStatus;
Anne Heimes's avatar
Anne Heimes committed
302 303 304 305
	oLog.dWorldTimeStamp = ITAClock::getDefaultClock( )->getTime( );
	oLog.uiBlockId = ++iAudioStreamingBlockID;
	oLog.iFreeSamples = GetRingBufferFreeSamples( );
	oLog.iNumSamplesTransmitted = iNumSamples;
Jonas Stienen's avatar
Jonas Stienen committed
306
	m_pNetworkStreamLogger->log( oLog );
Anne Heimes's avatar
Anne Heimes committed
307 308 309 310 311 312 313 314 315 316 317 318 319 320
	
	return GetRingBufferFreeSamples();
}

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

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

321
	int iFreeSamples = GetRingBufferSize() - ((m_iWriteCursor - m_iReadCursor + GetRingBufferSize()) % GetRingBufferSize());
Anne Heimes's avatar
Anne Heimes committed
322 323 324 325
	assert( iFreeSamples > 0 );
	return iFreeSamples;
}

326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
std::string CITANetAudioStream::GetStreamingStatusString( int iStreamingStatus )
{
	if( iStreamingStatus == CITANetAudioStream::INVALID )
		return "INVALID";
	if( iStreamingStatus == CITANetAudioStream::STOPPED )
		return "STOPPED";
	if( iStreamingStatus == CITANetAudioStream::CONNECTED )
		return "CONNECTED";
	if( iStreamingStatus == CITANetAudioStream::STREAMING )
		return "STREAMING";
	if( iStreamingStatus == CITANetAudioStream::BUFFER_UNDERRUN )
		return "BUFFER_UNDERRUN";
	if( iStreamingStatus == CITANetAudioStream::BUFFER_OVERRUN )
		return "BUFFER_OVERRUN";
	
	return "UNKOWN";
}

Jonas Stienen's avatar
Jonas Stienen committed
344 345 346 347 348 349 350 351
std::string CITANetAudioStream::GetNetAudioStreamLoggerBaseName() const
{
	return m_sNetAudioStreamLoggerBaseName;
}

void CITANetAudioStream::SetNetAudioStreamingLoggerBaseName( const std::string& sBaseName )
{
	m_sNetAudioStreamLoggerBaseName = sBaseName;
352 353
	m_pAudioStreamLogger->setOutputFile( GetNetAudioStreamLoggerBaseName() + "_AudioStream.log" );
	m_pNetworkStreamLogger->setOutputFile( GetNetAudioStreamLoggerBaseName() + "_NetworkStream.log" );
Jonas Stienen's avatar
Jonas Stienen committed
354 355
}

Anne Heimes's avatar
Anne Heimes committed
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
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;
}