Esempio n. 1
0
bool	ZKMORHP_IOThreadSlave::CalculateNextWakeUpTime(const AudioTimeStamp& inCurrentTime, AudioTimeStamp& outNextWakeUpTime, bool inMustResynch, bool& inIOGuardWasLocked)
{
	bool theAnswer = true;
	
//	static const Float64 kOverloadThreshold = 0.050;
	static const Float64 kOverloadThreshold = 0.000;
	bool isDone = false;
	AudioTimeStamp theCurrentTime = inCurrentTime;
	
	int theNumberIterations = 0;
	while(!isDone)
	{
		++theNumberIterations;
		Float64 theIOBufferFrameSize = mDevice->GetIOBufferFrameSize();
		
		//	set up the outNextWakeUpTime
		outNextWakeUpTime = CAAudioTimeStamp::kZero;
		outNextWakeUpTime.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid;
		
		//	set up the overload time
		AudioTimeStamp theOverloadTime = CAAudioTimeStamp::kZero;
		theOverloadTime.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid;
		
		//	calculate the sample time for the next wake up time
		AudioTimeStamp theSampleTime = mAnchorTime;
		theSampleTime.mFlags = kAudioTimeStampSampleTimeValid;
		theSampleTime.mSampleTime += mFrameCounter;
		theSampleTime.mSampleTime += theIOBufferFrameSize;
		
		//	translate that to a host time
		mDevice->TranslateTime(theSampleTime, outNextWakeUpTime);
		
		//	calculate the overload time
		Float64 theReservedAmount = std::max(0.0, mIOCycleUsage - kOverloadThreshold);
		theSampleTime = mAnchorTime;
		theSampleTime.mFlags = kAudioTimeStampSampleTimeValid;
		theSampleTime.mSampleTime += mFrameCounter;
		theSampleTime.mSampleTime += theReservedAmount * theIOBufferFrameSize;
		
		//	translate that to a host time
		mDevice->TranslateTime(theSampleTime, theOverloadTime);
		
		if(inMustResynch || (theCurrentTime.mHostTime >= theOverloadTime.mHostTime))
		{
			//	tell the device what happenned
			mDevice->GetIOCycleTelemetry().IOCycleWorkLoopOverloadBegin(mIOCycleCounter, theCurrentTime, theOverloadTime);
			
			//	the current time is beyond the overload time, have to resynchronize
			#if Log_Resynchs
				if(inMustResynch)
				{
					DebugMessageN1("ZKMORHP_IOThreadSlave::CalculateNextWakeUpTime: resynch was forced %d", theNumberIterations);
				}
				else
				{
					DebugMessageN1("ZKMORHP_IOThreadSlave::CalculateNextWakeUpTime: wake up time is in the past... resynching %d", theNumberIterations);
					DebugMessageN3("           Now: %qd Overload: %qd Difference: %qd", CAHostTimeBase::ConvertToNanos(theCurrentTime.mHostTime), CAHostTimeBase::ConvertToNanos(theOverloadTime.mHostTime), CAHostTimeBase::ConvertToNanos(theCurrentTime.mHostTime - theOverloadTime.mHostTime));
				}
			#endif
			
			//	notify clients that the overload has taken place
			if(inIOGuardWasLocked)
			{
				mIOGuard.Unlock();
			}
			CAPropertyAddress theOverloadAddress(kAudioDeviceProcessorOverload);
			mDevice->PropertiesChanged(1, &theOverloadAddress);
			inIOGuardWasLocked = mIOGuard.Lock();

			//	re-anchor at the current time
			theCurrentTime.mSampleTime = 0;
			theCurrentTime.mHostTime = 0;
			if(mDevice->EstablishIOCycleAnchorTime(theCurrentTime))
			{
				Resynch(&theCurrentTime, false);
			}
			else
			{
				theAnswer = false;
				isDone = true;
			}
			
			//	reset the forced resynch flag
			inMustResynch = false;
			mDevice->GetIOCycleTelemetry().IOCycleWorkLoopOverloadEnd(mIOCycleCounter, mAnchorTime);
		}
		else
		{
			//	still within the limits
			isDone = true;
		}
	}
	
	//  adjust the counter depending on what happenned
	if(theNumberIterations > 1)
	{
		//  we went through the calculation more than once, which means an overload happenned
		++mOverloadCounter;
	}
	else
	{
		//  only did the calculation once, so no overload occurred
		mOverloadCounter = 0;
	}
	
	return theAnswer;
}
Esempio n. 2
0
void	ZKMORHP_IOThreadSlave::WorkLoopIteration(bool& isInNeedOfResynch)
{
	if (mStopWorkLoop) {
		WorkLoopTeardown();
		return;
	}
	
	try	{
		bool wasLocked = mIOGuard.Lock();
//		bool wasLocked;
		wasLocked = mIOGuard.Try(wasLocked);
		
		//	get the current time
		AudioTimeStamp theCurrentTime;
		mDevice->GetCurrentTime(theCurrentTime);
		
			//	increment the counter
			++mIOCycleCounter;
			
		//	do IO if the thread wasn't stopped
		if(!mStopWorkLoop)
		{
			if(theCurrentTime.mSampleTime >= (mAnchorTime.mSampleTime + mFrameCounter))
			{
				//	increment the frame counter
				mFrameCounter += mDevice->GetIOBufferFrameSize();
			
				//	the new cycle is starting
				mDevice->GetIOCycleTelemetry().IOCycleWorkLoopBegin(mIOCycleCounter, theCurrentTime);
				if(mDevice->UpdateIOCycleTimingServices())
				{
					//	something unexpected happened with the time stamp, so resynch prior to doing IO
					AudioTimeStamp theNewAnchor = CAAudioTimeStamp::kZero;
					theNewAnchor.mSampleTime = 0;
					theNewAnchor.mHostTime = 0;
					theNewAnchor.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid;
					if(mDevice->EstablishIOCycleAnchorTime(theNewAnchor))
					{
						Resynch(&theNewAnchor, false);
					}
					else
					{
						Resynch(NULL, false);
					}
					
					//	re-get the current time too
					mDevice->GetCurrentTime(theCurrentTime);
				}
			
				//	do the IO
				isInNeedOfResynch = PerformIO(theCurrentTime);
			}
		}
		
		//	calculate the next wake up time
		AudioTimeStamp theNextWakeUpTime = CAAudioTimeStamp::kZero;
		theNextWakeUpTime.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid;
//		bool wasLocked = false;
//		bool wasLocked = mIOGuard.Lock();
		if(CalculateNextWakeUpTime(theCurrentTime, theNextWakeUpTime, isInNeedOfResynch, wasLocked))
		{
			mDevice->GetIOCycleTelemetry().IOCycleWorkLoopEnd(mIOCycleCounter, theCurrentTime, theNextWakeUpTime);
		}
		
		//	execute any deferred commands
		mDevice->ExecuteAllCommands();
		if (wasLocked) mIOGuard.Unlock();
	} 
	catch(const CAException& inException)
	{
		DebugMessageN1("ZKMORHP_IOThreadSlave::WorkLoopIteration: Caught a CAException, code == %ld", (long int)inException.GetError());
	}
	catch(...)
	{
		DebugMessage("ZKMORHP_IOThreadSlave::WorkLoopIteration: Caught an unknown exception.");
	}
}
Esempio n. 3
0
void	ZKMORHP_IOThreadSlave::WorkLoop()
{
	//	grab the IO guard
	bool wasLocked = mIOGuard.Lock();
	
	//	initialize some stuff
	mWorkLoopPhase = kInitializingPhase;
	mIOCycleCounter = 0;
	mOverloadCounter = 0;
	CAPropertyAddress theIsRunningAddress(kAudioDevicePropertyDeviceIsRunning);
	mDevice->GetIOCycleTelemetry().IOCycleInitializeBegin(mIOCycleCounter);
		
	try
	{
		//	and signal that the IO thread is running
		mIOGuard.NotifyAll();
		
		//	initialize the work loop stopping conditions
		mStopWorkLoop = false;
		
		//	Tell the device that the IO thread has initialized. Note that we unlock around this call
		//	due to the fact that IOCycleInitialize might not return for a while because it might
		//	have to wait for the hardware to start.
		if(wasLocked)
		{
			mIOGuard.Unlock();
		}
		
		//	tell the device that the IO cycle is initializing to start the timing services
		mDevice->StartIOCycleTimingServices();
		
		//	set the device state to know the engine is running
		mDevice->IOEngineStarted();
		
		//	notify clients that the engine is running
		mDevice->PropertiesChanged(1, &theIsRunningAddress);
		
		//	re-lock the guard
		wasLocked = mIOGuard.Lock();

		//	make sure the thread is still running before moving on
		if(!mStopWorkLoop)
		{
			//	set the time constraints for the IOThread
			SetTimeConstraints();
			
			//	initialize the clock
			mDevice->EstablishIOCycleAnchorTime(mAnchorTime);
			mFrameCounter = 0;
			
#if	Offset_For_Input
			if(mDevice->HasInputStreams())
			{
				//	the first sleep cycle as to be at least the input safety offset and a buffer's
				//	worth of time to be sure that the input data is all there
				mFrameCounter += mDevice->GetSafetyOffset(true);
			}
#endif
			
			//	enter the work loop
			mWorkLoopPhase = kRunningPhase;
			bool isInNeedOfResynch = false;
			mDevice->GetIOCycleTelemetry().IOCycleInitializeEnd(mIOCycleCounter, mAnchorTime);
			while(!mStopWorkLoop)
			{
				//	get the current time
				AudioTimeStamp theCurrentTime;
				mDevice->GetCurrentTime(theCurrentTime);
				
				//	calculate the next wake up time
				AudioTimeStamp theNextWakeUpTime = CAAudioTimeStamp::kZero;
				theNextWakeUpTime.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid;
				if(CalculateNextWakeUpTime(theCurrentTime, theNextWakeUpTime, isInNeedOfResynch, wasLocked))
				{
					//	sleep until the  next wake up time
					mDevice->GetIOCycleTelemetry().IOCycleWorkLoopEnd(mIOCycleCounter, theCurrentTime, theNextWakeUpTime);
					mIOGuard.WaitUntil(CAHostTimeBase::ConvertToNanos(theNextWakeUpTime.mHostTime));
					
					//	increment the counter
					++mIOCycleCounter;
					
					//	do IO if the thread wasn't stopped
					if(!mStopWorkLoop)
					{
						//	get the current time
						mDevice->GetCurrentTime(theCurrentTime);
						
						#if Log_SchedulingLatency
							//	check to see if we have incurred a large scheduling latency
							if(theCurrentTime.mHostTime > (theNextWakeUpTime.mHostTime + mAllowedLatency))
							{
								//	log it
								mLatencyLog->Capture(theNextWakeUpTime.mHostTime - mAllowedLatency, theCurrentTime.mHostTime, true);
								
								//	print how late we are
								DebugMessageN1("HP_IOThread::WorkLoop: woke up late by %f milliseconds", ((Float64)CAHostTimeBase::ConvertToNanos(theCurrentTime.mHostTime - theNextWakeUpTime.mHostTime)) / (1000.0 * 1000.0));
							}
						#endif
						
						if(theCurrentTime.mSampleTime >= (mAnchorTime.mSampleTime + mFrameCounter))
						{
							//	increment the frame counter
							mFrameCounter += mDevice->GetIOBufferFrameSize();
						
							//	the new cycle is starting
							mDevice->GetIOCycleTelemetry().IOCycleWorkLoopBegin(mIOCycleCounter, theCurrentTime);
							if(mDevice->UpdateIOCycleTimingServices())
							{
								//	something unexpected happenned with the time stamp, so resynch prior to doing IO
								AudioTimeStamp theNewAnchor = CAAudioTimeStamp::kZero;
								theNewAnchor.mSampleTime = 0;
								theNewAnchor.mHostTime = 0;
								theNewAnchor.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid;
								if(mDevice->EstablishIOCycleAnchorTime(theNewAnchor))
								{
									Resynch(&theNewAnchor, false);
								}
								else
								{
									Resynch(NULL, false);
								}
								
								//	re-get the current time too
								mDevice->GetCurrentTime(theCurrentTime);
							}
						
							//	do the IO
							isInNeedOfResynch = PerformIO(theCurrentTime);
						}
					}
				}
				else
				{
					//	calculating the next wake up time failed, so we just stop everything (which
					//	will get picked up when the commands are executed
					mDevice->ClearAllCommands();
					mDevice->Do_StopAllIOProcs();
				}
				
				//	execute any deferred commands
				mDevice->ExecuteAllCommands();
			}
		}
	
		mWorkLoopPhase = kTeardownPhase;
		mDevice->GetIOCycleTelemetry().IOCycleTeardownBegin(mIOCycleCounter);

		//	the work loop has finished, clear the time constraints
		ClearTimeConstraints();
		
		//	tell the device that the IO thread is torn down
		mDevice->StopIOCycleTimingServices();
	}
	catch(const CAException& inException)
	{
		DebugMessageN1("HP_IOThread::WorkLoop: Caught a CAException, code == %ld", (long int)inException.GetError());
	}
	catch(...)
	{
		DebugMessage("HP_IOThread::WorkLoop: Caught an unknown exception.");
	}
	
	//	set the device state to know the engine has stopped
	mDevice->IOEngineStopped();
		
	//	Notify clients that the IO thread is stopping. Note that we unlock around this call
	//	due to the fact that clients might want to call back into the HAL.
	if(wasLocked)
	{
		mIOGuard.Unlock();
	}

	//	Notify clients that the IO thread is stopping
	mDevice->PropertiesChanged(1, &theIsRunningAddress);
		
	//	re-lock the guard
	wasLocked = mIOGuard.Lock();

	mDevice->GetIOCycleTelemetry().IOCycleTeardownEnd(mIOCycleCounter);
	mWorkLoopPhase = kNotRunningPhase;
	mIOGuard.NotifyAll();
	mIOCycleCounter = 0;
	
	if(wasLocked)
	{
		mIOGuard.Unlock();
	}
}
Esempio n. 4
0
void	HP_IOThread::WorkLoop()
{
	//	grab the IO guard
	bool wasLocked = mIOGuard.Lock();
	
	//	initialize some stuff
	mWorkLoopPhase = kInitializingPhase;
	mIOCycleCounter = 0;
	mOverloadCounter = 0;
	CAPropertyAddress theIsRunningAddress(kAudioDevicePropertyDeviceIsRunning);
#if Use_HAL_Telemetry
	mDevice->GetIOCycleTelemetry().IOCycleInitializeBegin(mIOCycleCounter);
#else
	HAL_IOCYCLEINITIALIZEBEGIN(mIOCycleCounter);
#endif
	try
	{
		//	and signal that the IO thread is running
		mIOGuard.NotifyAll();
		
		//	initialize the work loop stopping conditions
		mStopWorkLoop = false;
		
		//	Tell the device that the IO thread has initialized. Note that we unlock around this call
		//	due to the fact that IOCycleInitialize might not return for a while because it might
		//	have to wait for the hardware to start.
		if(wasLocked)
		{
			mIOGuard.Unlock();
		}
		
		//	tell the device that the IO cycle is initializing to start the timing services
		mDevice->StartIOCycleTimingServices();
		
		//	set the device state to know the engine is running
		mDevice->IOEngineStarted();
		
		//	notify clients that the engine is running
		mDevice->PropertiesChanged(1, &theIsRunningAddress);
		
		//	re-lock the guard
		wasLocked = mIOGuard.Lock();

		//	make sure the thread is still running before moving on
		if(!mStopWorkLoop)
		{
			//	set the time constraints for the IOThread
			SetTimeConstraints();
			
			//	initialize the clock
			mDevice->EstablishIOCycleAnchorTime(mAnchorTime);
			mFrameCounter = 0;
			
#if	Offset_For_Input
			if(mDevice->HasInputStreams())
			{
				//	the first sleep cycle as to be at least the input safety offset and a buffer's
				//	worth of time to be sure that the input data is all there
				mFrameCounter += mDevice->GetSafetyOffset(true);
			}
#endif
			
			//	get the current time
			AudioTimeStamp theCurrentTime;
			mDevice->GetCurrentTime(theCurrentTime);
				
			//	enter the work loop
			mWorkLoopPhase = kRunningPhase;
			bool isInNeedOfResynch = false;
			bool isCheckingForOverloads = false;
			Float64 theIOBufferFrameSize = mDevice->GetIOBufferFrameSize();
#if Use_HAL_Telemetry
			mDevice->GetIOCycleTelemetry().IOCycleInitializeEnd(mIOCycleCounter, mAnchorTime);
			mDevice->GetIOCycleTelemetry().IOCycleWorkLoopBegin(mIOCycleCounter, theCurrentTime);
#else
			HAL_IOCYCLEINITIALIZEEND(mIOCycleCounter, mAnchorTime);
			HAL_IOCYCLEWORKLOOPBEGIN(mIOCycleCounter, theCurrentTime);
#endif
			while(!mStopWorkLoop)
			{
				//	get the new IO buffer frame size
				Float64 theNewIOBufferFrameSize = mDevice->GetIOBufferFrameSize();
				
				//	initialize the next wake up time
				AudioTimeStamp theNextWakeUpTime = CAAudioTimeStamp::kZero;
				theNextWakeUpTime.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid;
				
				//	get the current time
				mDevice->GetCurrentTime(theCurrentTime);
				
				//	we have to run a special, untimed IO cycle if the IO buffer size changed
				if((theNewIOBufferFrameSize != theIOBufferFrameSize) && (theCurrentTime.mSampleTime >= (mAnchorTime.mSampleTime + mFrameCounter)))
				{
					//	mark the end of the previous cycle
#if Use_HAL_Telemetry
					mDevice->GetIOCycleTelemetry().IOCycleWorkLoopEnd(mIOCycleCounter, theCurrentTime, theNextWakeUpTime);
#else
					HAL_IOCYCLEWORKLOOPEND(mIOCycleCounter, theCurrentTime, theNextWakeUpTime);
#endif
					//	increment the cycle counter
					++mIOCycleCounter;
					
					//	increment the frame counter
					mFrameCounter += theIOBufferFrameSize;
				
					//	the new cycle is starting
#if Use_HAL_Telemetry
					mDevice->GetIOCycleTelemetry().IOCycleWorkLoopBegin(mIOCycleCounter, theCurrentTime);
#else
					HAL_IOCYCLEWORKLOOPBEGIN(mIOCycleCounter, theCurrentTime);
#endif

					//	do the IO, note that we don't need to update the timing services for this special cycle
					isInNeedOfResynch = PerformIO(theCurrentTime, theIOBufferFrameSize);
					
					//	turn off overload checking for the next cycle to be nice to clients
					isCheckingForOverloads = false;
				}
				
				//	calculate the next wake up time
				if(CalculateNextWakeUpTime(theCurrentTime, theIOBufferFrameSize, theNextWakeUpTime, isCheckingForOverloads, isInNeedOfResynch, wasLocked))
				{
					//	sleep until the  next wake up time
#if Use_HAL_Telemetry
					mDevice->GetIOCycleTelemetry().IOCycleWorkLoopEnd(mIOCycleCounter, theCurrentTime, theNextWakeUpTime);
#else
					HAL_IOCYCLEWORKLOOPEND(mIOCycleCounter, theCurrentTime, theNextWakeUpTime);
#endif

					mIOGuard.WaitUntil(CAHostTimeBase::ConvertToNanos(theNextWakeUpTime.mHostTime));
					
					//	increment the cycle counter
					++mIOCycleCounter;
					
					//	make sure overload checking is enabled
					isCheckingForOverloads = true;
					
					//	do IO if the thread wasn't stopped
					if(!mStopWorkLoop)
					{
						//	get the current time
						mDevice->GetCurrentTime(theCurrentTime);
						
						if(theCurrentTime.mSampleTime >= (mAnchorTime.mSampleTime + mFrameCounter))
						{
							//	increment the frame counter
							mFrameCounter += theIOBufferFrameSize;
						
							//	refresh the current buffer size
							theIOBufferFrameSize = theNewIOBufferFrameSize;
							
							//	the new cycle is starting
#if Use_HAL_Telemetry
							mDevice->GetIOCycleTelemetry().IOCycleWorkLoopBegin(mIOCycleCounter, theCurrentTime);
#else
							HAL_IOCYCLEWORKLOOPBEGIN(mIOCycleCounter, theCurrentTime);
#endif

							if(mDevice->UpdateIOCycleTimingServices())
							{
								//	something unexpected happenned with the time stamp, so resynch prior to doing IO
								AudioTimeStamp theNewAnchor = CAAudioTimeStamp::kZero;
								theNewAnchor.mSampleTime = 0;
								theNewAnchor.mHostTime = 0;
								theNewAnchor.mFlags = kAudioTimeStampSampleTimeValid + kAudioTimeStampHostTimeValid + kAudioTimeStampRateScalarValid;
								if(mDevice->EstablishIOCycleAnchorTime(theNewAnchor))
								{
									Resynch(&theNewAnchor, false);
								}
								else
								{
									Resynch(NULL, false);
								}
								
								//	re-get the current time too
								mDevice->GetCurrentTime(theCurrentTime);
								
								//	mark the telemetry
#if Use_HAL_Telemetry
								mDevice->GetIOCycleTelemetry().Resynch(GetIOCycleNumber(), mAnchorTime);
#else
								HAL_RESYNCH(GetIOCycleNumber(), mAnchorTime);
#endif
							}
						
							//	do the IO
							isInNeedOfResynch = PerformIO(theCurrentTime, theIOBufferFrameSize);
						}
					}
				}
				else
				{
					//	calculating the next wake up time failed, so we just stop everything (which
					//	will get picked up when the commands are executed
					mDevice->ClearAllCommands();
					mDevice->Do_StopAllIOProcs();
				}
				
				//	execute any deferred commands
				mDevice->ExecuteAllCommands();
			}
		}
	
		mWorkLoopPhase = kTeardownPhase;
#if Use_HAL_Telemetry
		mDevice->GetIOCycleTelemetry().IOCycleTeardownBegin(mIOCycleCounter);
#else
		HAL_IOCYCLETEARDOWNBEGIN(mIOCycleCounter);
#endif

		//	the work loop has finished, clear the time constraints
		ClearTimeConstraints();
		
		//	tell the device that the IO thread is torn down
		mDevice->StopIOCycleTimingServices();
	}
	catch(const CAException& inException)
	{
		DebugMessageN1("HP_IOThread::WorkLoop: Caught a CAException, code == %ld", (long int)inException.GetError());
	}
	catch(...)
	{
		DebugMessage("HP_IOThread::WorkLoop: Caught an unknown exception.");
	}
	
	//	set the device state to know the engine has stopped
	mDevice->IOEngineStopped();
		
	//	Notify clients that the IO thread is stopping. Note that we unlock around this call
	//	due to the fact that clients might want to call back into the HAL.
	if(wasLocked)
	{
		mIOGuard.Unlock();
	}

	//	Notify clients that the IO thread is stopping
	mDevice->PropertiesChanged(1, &theIsRunningAddress);
		
	//	re-lock the guard
	wasLocked = mIOGuard.Lock();

#if Use_HAL_Telemetry
	mDevice->GetIOCycleTelemetry().IOCycleTeardownEnd(mIOCycleCounter);
#else
	HAL_IOCYCLETEARDOWNEND(mIOCycleCounter);
#endif

	mWorkLoopPhase = kNotRunningPhase;
	mIOGuard.NotifyAll();
	mIOCycleCounter = 0;
	
	if(wasLocked)
	{
		mIOGuard.Unlock();
	}
}