Exemplo n.º 1
0
void	ZKMORHP_IOThreadSlave::WorkLoopTeardown()
{
	// The other thread holds the lock during teardown
	try {
		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("ZKMORHP_IOThreadSlave::WorkLoopTeardown: Caught a CAException, code == %ld", (long int)inException.GetError());
	}
	catch(...)
	{
		DebugMessage("ZKMORHP_IOThreadSlave::WorkLoopTeardown: Caught an unknown exception.");
	}
	
	//	set the device state to know the engine has stopped
	mDevice->IOEngineStopped();
		
	//	Notify clients that the IO thread is stopping
	CAPropertyAddress theIsRunningAddress(kAudioDevicePropertyDeviceIsRunning);
	mDevice->PropertiesChanged(1, &theIsRunningAddress);

	mDevice->GetIOCycleTelemetry().IOCycleTeardownEnd(mIOCycleCounter);
	mWorkLoopPhase = kNotRunningPhase;
	mIOGuard.NotifyAll();
	mIOCycleCounter = 0;
}
Exemplo n.º 2
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();
	}
}
Exemplo n.º 3
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();
	}
}