Пример #1
0
/*
============
Cbuf_ExecuteText
============
*/
void Cbuf_ExecuteText (int exec_when, const char *text)
{
	switch (exec_when)
	{
	case EXEC_NOW:
		
		Sys_EnterCriticalSection(CRIT_CBUF);
		
		if (text && strlen(text) > 0) {
			Com_DPrintf(S_COLOR_YELLOW "EXEC_NOW %s\n", text);
			Cmd_ExecuteString (text);
		} else {
			Cbuf_Execute();
			Com_DPrintf(S_COLOR_YELLOW "EXEC_NOW %s\n", cmd_text.data);
		}
		
		Sys_LeaveCriticalSection(CRIT_CBUF);
			
		break;
	case EXEC_INSERT:
		Cbuf_InsertText (text);
		break;
	case EXEC_APPEND:
		Cbuf_AddText (text);
		break;
	default:
		Com_Error (ERR_FATAL, "Cbuf_ExecuteText: bad exec_when");
	}
}
Пример #2
0
/*
==================
Sys_DestroyThread
==================
*/
void Sys_DestroyThread(xthreadInfo& info) {
	assert(info.threadHandle);

	SDL_WaitThread(info.threadHandle, NULL);

	info.name = NULL;
	info.threadHandle = NULL;
	info.threadId = 0;

	Sys_EnterCriticalSection();

	for (int i = 0; i < thread_count; i++) {
		if (&info == thread[i]) {
			thread[i] = NULL;

			int j;
			for (j = i + 1; j < thread_count; j++)
				thread[j - 1] = thread[j];

			thread[j - 1] = NULL;
			thread_count--;

			break;
		}
	}

	Sys_LeaveCriticalSection( );
}
Пример #3
0
/*
===================
idSoundEmitterLocal::StopSound

can pass SCHANNEL_ANY
===================
*/
void idSoundEmitterLocal::StopSound( const s_channelType channel ) {
	int i;
	if( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
		common->Printf( "StopSound(%i,%i)\n", index, channel );
	}
	if( soundWorld && soundWorld->writeDemo ) {
		soundWorld->writeDemo->WriteInt( DS_SOUND );
		soundWorld->writeDemo->WriteInt( SCMD_STOP );
		soundWorld->writeDemo->WriteInt( index );
		soundWorld->writeDemo->WriteInt( channel );
	}
	Sys_EnterCriticalSection();
	for( i = 0; i < SOUND_MAX_CHANNELS; i++ ) {
		idSoundChannel	*chan = &channels[i];
		if( !chan->triggerState ) {
			continue;
		}
		if( channel != SCHANNEL_ANY && chan->triggerChannel != channel ) {
			continue;
		}
		// stop it
		chan->Stop();
		// free hardware resources
		chan->ALStop();
		// if this was an onDemand sound, purge the sample now
		if( chan->leadinSample->onDemand ) {
			chan->leadinSample->PurgeSoundSample();
		}
		chan->leadinSample = NULL;
		chan->soundShader = NULL;
	}
	Sys_LeaveCriticalSection();
}
void Com_PrintLogfile( const char *msg )
{

	Sys_EnterCriticalSection(5);

	if ( com_logfile && com_logfile->integer ) {
        // TTimo: only open the qconsole.log if the filesystem is in an initialized state
        // also, avoid recursing in the qconsole.log opening (i.e. if fs_debug is on)
	    if ( !logfile && FS_Initialized()) {
			struct tm *newtime;
			time_t aclock;

			time( &aclock );
			newtime = localtime( &aclock );

			logfile = FS_FOpenFileWrite( "qconsole.log" );

			if ( com_logfile->integer > 1 && logfile ) {
				// force it to not buffer so we get valid
				// data even if we are crashing
				FS_ForceFlush(logfile);
			}
			if ( logfile ) FS_Write(va("\nLogfile opened on %s\n", asctime( newtime )), strlen(va("\nLogfile opened on %s\n", asctime( newtime ))), logfile);
	    }
	    if ( logfile && FS_Initialized()) 
	    {
	    	FS_Write(msg, strlen(msg), logfile);
	    }
	}
	Sys_LeaveCriticalSection(5);
}
Пример #5
0
/*
==================
Sys_GetThreadName
find the name of the calling thread
==================
*/
const char* Sys_GetThreadName( int *index ) {
	Sys_EnterCriticalSection( );
	pthread_t thread = pthread_self();
	for( int i = 0 ; i < g_thread_count ; i++ ) {
		if ( thread == (pthread_t)g_threads[ i ]->threadHandle ) {
			if ( index ) {
				*index = i;
			}
			Sys_LeaveCriticalSection( );
			return g_threads[ i ]->name;
		}
	}
	if ( index ) {
		*index = -1;
	}
	Sys_LeaveCriticalSection( );
	return "main";
}
/*
=================
Sys_Print
=================
*/
void Sys_Print( const char *msg )
{
	Sys_EnterCriticalSection(CRIT_CONSOLE);

//	CON_LogWrite( msg );
	CON_Print( msg );

	Sys_LeaveCriticalSection(CRIT_CONSOLE);

}
Пример #7
0
/*
==================
Sys_TriggerEvent
==================
*/
void Sys_TriggerEvent( int index ) {
	assert( index >= 0 && index < MAX_TRIGGER_EVENTS );
	Sys_EnterCriticalSection( MAX_LOCAL_CRITICAL_SECTIONS - 1 );
	if ( waiting[ index ] ) {		
		pthread_cond_signal( &event_cond[ index ] );
	} else {
		// emulate windows behaviour: if no thread is waiting, leave the signal on so next wait keeps going
		signaled[ index ] = true;
	}
	Sys_LeaveCriticalSection( MAX_LOCAL_CRITICAL_SECTIONS - 1 );
}
Пример #8
0
/*
==================
Sys_CreateThread
==================
*/
void Sys_CreateThread(xthread_t function, void *parms, xthreadInfo& info, const char *name) {
	Sys_EnterCriticalSection();

	SDL_Thread *t = SDL_CreateThread(function, parms);

	if (!t) {
		common->Error("ERROR: SDL_thread for '%s' failed\n", name);
		Sys_LeaveCriticalSection();
		return;
	}

	info.name = name;
	info.threadHandle = t;
	info.threadId = SDL_GetThreadID(t);

	if (thread_count < MAX_THREADS)
		thread[thread_count++] = &info;
	else
		common->DPrintf("WARNING: MAX_THREADS reached\n");

	Sys_LeaveCriticalSection();
}
Пример #9
0
/*
====================
idWaveFile::CloseOGG
====================
*/
int idWaveFile::CloseOGG( void ) {
	OggVorbis_File *ov = (OggVorbis_File *) ogg;
	if ( ov != NULL ) {
		Sys_EnterCriticalSection( CRITICAL_SECTION_ONE );
		ov_clear( ov );
		delete ov;
		Sys_LeaveCriticalSection( CRITICAL_SECTION_ONE );
		fileSystem->CloseFile( mhmmio );
		mhmmio = NULL;
		ogg = NULL;
		return 0;
	}
	return -1;
}
Пример #10
0
/*
==================
Sys_WaitForEvent
==================
*/
void Sys_WaitForEvent( int index ) {
	assert( index >= 0 && index < MAX_TRIGGER_EVENTS );
	Sys_EnterCriticalSection( MAX_LOCAL_CRITICAL_SECTIONS - 1 );
	assert( !waiting[ index ] );	// WaitForEvent from multiple threads? that wouldn't be good
	if ( signaled[ index ] ) {
		// emulate windows behaviour: signal has been raised already. clear and keep going
		signaled[ index ] = false;
	} else {
		waiting[ index ] = true;
		pthread_cond_wait( &event_cond[ index ], &global_lock[ MAX_LOCAL_CRITICAL_SECTIONS - 1 ] );
		waiting[ index ] = false;
	}
	Sys_LeaveCriticalSection( MAX_LOCAL_CRITICAL_SECTIONS - 1 );
}
Пример #11
0
/*
==========
idAudioHardwareOSX::DeviceIOProc
==========
*/
OSStatus idAudioHardwareOSX::DeviceIOProc( AudioDeviceID			inDevice,
		const AudioTimeStamp	*inNow,
		const AudioBufferList	*inInputData,
		const AudioTimeStamp	*inInputTime,
		AudioBufferList			*outOutputData,
		const AudioTimeStamp	*inOutputTime,
		void					*inClientData ) {
	// setup similar to async thread
	Sys_EnterCriticalSection();
	soundSystem->AsyncMix( ( int )inOutputTime->mSampleTime, ( float * )outOutputData->mBuffers[ 0 ].mData );
	Sys_LeaveCriticalSection();
	// doom mixes sound to -32768.0f 32768.0f range, scale down to -1.0f 1.0f
	SIMDProcessor->Mul( ( Float32 * )outOutputData->mBuffers[ 0 ].mData, 1.0f / 32768.0f, ( Float32 * )outOutputData->mBuffers[ 0 ].mData, MIXBUFFER_SAMPLES * 2 );
	return kAudioHardwareNoError;
}
Пример #12
0
/*
============
Cbuf_AddText

Adds command text at the end of the buffer, does NOT add a final \n
============
*/
void Cbuf_AddText( const char *text ) {
	int		len;
	byte*		new_buf;
	len = strlen (text) +1;

	Sys_EnterCriticalSection(CRIT_CBUF);
	
	if ( len + cmd_text.cursize > cmd_text.maxsize ) {

		if(cmd_text.data == cmd_text_buf)
		{
			new_buf = realloc(NULL, len + cmd_text.cursize);
			if(new_buf != NULL)
			{
				Com_Memcpy(new_buf, cmd_text_buf, len + cmd_text.cursize);
			}
		}else{
			new_buf = realloc(cmd_text.data, len + cmd_text.cursize);
		}

		if(new_buf == NULL)
		{
			Com_PrintError( "Cbuf_AddText overflowed ; realloc failed\n" );
			Sys_LeaveCriticalSection(CRIT_CBUF);
			return;
		}
		cmd_text.data = new_buf;
		cmd_text.maxsize = len + cmd_text.cursize;
	}

	Com_Memcpy(&cmd_text.data[cmd_text.cursize], text, len -1);
	cmd_text.cursize += len -1;

	Sys_LeaveCriticalSection(CRIT_CBUF);
	
}
Пример #13
0
/*
==================
Sys_TriggerEvent
==================
*/
void Sys_TriggerEvent(int index) {
	assert(index >= 0 && index < MAX_TRIGGER_EVENTS);

	Sys_EnterCriticalSection(CRITICAL_SECTION_SYS);

	if (waiting[index]) {
		if (SDL_CondSignal(cond[index]) != 0)
			common->Error("ERROR: SDL_CondSignal failed\n");
	} else {
		// emulate windows behaviour: if no thread is waiting, leave the signal on so next wait keeps going
		signaled[index] = true;
	}

	Sys_LeaveCriticalSection(CRITICAL_SECTION_SYS);
}
Пример #14
0
/*
==================
Sys_WaitForEvent
==================
*/
void Sys_WaitForEvent(int index) {
	assert(index >= 0 && index < MAX_TRIGGER_EVENTS);

	Sys_EnterCriticalSection(CRITICAL_SECTION_SYS);

	assert(!waiting[index]);	// WaitForEvent from multiple threads? that wouldn't be good
	if (signaled[index]) {
		// emulate windows behaviour: signal has been raised already. clear and keep going
		signaled[index] = false;
	} else {
		waiting[index] = true;
		if (SDL_CondWait(cond[index], mutex[CRITICAL_SECTION_SYS]) != 0)
			common->Error("ERROR: SDL_CondWait failed\n");
		waiting[index] = false;
	}

	Sys_LeaveCriticalSection(CRITICAL_SECTION_SYS);
}
Пример #15
0
/*
====================
idSampleDecoderLocal::ClearDecoder
====================
*/
void idSampleDecoderLocal::ClearDecoder( void ) {
	Sys_EnterCriticalSection( CRITICAL_SECTION_ONE );

	switch( lastFormat ) {
		case WAVE_FORMAT_TAG_PCM: {
			break;
		}
		case WAVE_FORMAT_TAG_OGG: {
			ov_clear( &ogg );
			memset( &ogg, 0, sizeof( ogg ) );
			break;
		}
	}

	Clear();

	Sys_LeaveCriticalSection( CRITICAL_SECTION_ONE );
}
void QDECL SV_EnterLeaveLog( const char *fmt, ... ) {

	Sys_EnterCriticalSection(5);

	va_list		argptr;
	char		msg[MAXPRINTMSG];
	char		inputmsg[MAXPRINTMSG];
	struct tm 	*newtime;
	char*		ltime;
	time_t		realtime;

        // logfile
	if ( com_logfile && com_logfile->integer ) {
        // TTimo: only open the qconsole.log if the filesystem is in an initialized state
        // also, avoid recursing in the qconsole.log opening (i.e. if fs_debug is on)

	    va_start (argptr,fmt);
	    Q_vsnprintf (inputmsg, sizeof(inputmsg), fmt, argptr);
	    va_end (argptr);

	    Com_UpdateRealtime();
	    realtime = Com_GetRealtime();
	    newtime = localtime( &realtime );
	    ltime = asctime( newtime );
	    ltime[strlen(ltime)-1] = 0;

	    if ( !enterleavelogfile && FS_Initialized()) {

			enterleavelogfile = FS_FOpenFileAppend( "enterleave.log" );
			// force it to not buffer so we get valid
			if ( enterleavelogfile ){
				FS_ForceFlush(enterleavelogfile);
				FS_Write(va("\nLogfile opened on %s\n\n", ltime), strlen(va("\nLogfile opened on %s\n\n", ltime)), enterleavelogfile);
			}
	    }

	    if ( enterleavelogfile && FS_Initialized()) {
		Com_sprintf(msg, sizeof(msg), "%s: %s\n", ltime, inputmsg);
		FS_Write(msg, strlen(msg), enterleavelogfile);
	    }

	}
	Sys_LeaveCriticalSection(5);
}
Пример #17
0
/*
==================
Sys_CreateThread
==================
*/
void Sys_CreateThread( xthread_t function, void *parms, xthreadPriority priority, xthreadInfo& info, const char *name, xthreadInfo **threads, int *thread_count ) {
	Sys_EnterCriticalSection( );		
	pthread_attr_t attr;
	pthread_attr_init( &attr );
	if ( pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ) != 0 ) {
		common->Error( "ERROR: pthread_attr_setdetachstate %s failed\n", name );
	}
	if ( pthread_create( ( pthread_t* )&info.threadHandle, &attr, ( pthread_function_t )function, parms ) != 0 ) {
		common->Error( "ERROR: pthread_create %s failed\n", name );
	}
	pthread_attr_destroy( &attr );
	info.name = name;
	if ( *thread_count < MAX_THREADS ) {
		threads[ ( *thread_count )++ ] = &info;
	} else {
		common->DPrintf( "WARNING: MAX_THREADS reached\n" );
	}
	Sys_LeaveCriticalSection( );
}
void QDECL Com_PrintAdministrativeLog( const char *msg ) {

	Sys_EnterCriticalSection(5);

	struct tm 	*newtime;
	char*		ltime;
	time_t		realtime;

        // logfile
	if ( com_logfile && com_logfile->integer ) {
        // TTimo: only open the qconsole.log if the filesystem is in an initialized state
        //   also, avoid recursing in the qconsole.log opening (i.e. if fs_debug is on)


	    if ( !adminlogfile && FS_Initialized()) {

			Com_UpdateRealtime();
			realtime = Com_GetRealtime();
			newtime = localtime( &realtime );
			ltime = asctime( newtime );
			ltime[strlen(ltime)-1] = 0;


			adminlogfile = FS_FOpenFileAppend( "adminactions.log" );
			// force it to not buffer so we get valid
			if ( adminlogfile ){
				FS_ForceFlush(adminlogfile);
				FS_Write(va("\nLogfile opened on %s\n\n", ltime), strlen(va("\nLogfile opened on %s\n\n", ltime)), adminlogfile);
			}
	    }

	    if ( adminlogfile && FS_Initialized())
	    {
		FS_Write(msg, strlen(msg), adminlogfile);
	    }

	}
	Sys_LeaveCriticalSection(5);
}
Пример #19
0
/*
====================
idSampleDecoderLocal::Decode
====================
*/
void idSampleDecoderLocal::Decode( idSoundSample *sample, int sampleOffset44k, int sampleCount44k, float *dest ) {
	int readSamples44k;

	if ( sample->objectInfo.wFormatTag != lastFormat || sample != lastSample ) {
		ClearDecoder();
	}

	lastDecodeTime = soundSystemLocal.CurrentSoundTime;

	if ( failed ) {
		memset( dest, 0, sampleCount44k * sizeof( dest[0] ) );
		return;
	}

	// samples can be decoded both from the sound thread and the main thread for shakes
	Sys_EnterCriticalSection( CRITICAL_SECTION_ONE );

	switch( sample->objectInfo.wFormatTag ) {
		case WAVE_FORMAT_TAG_PCM: {
			readSamples44k = DecodePCM( sample, sampleOffset44k, sampleCount44k, dest );
			break;
		}
		case WAVE_FORMAT_TAG_OGG: {
			readSamples44k = DecodeOGG( sample, sampleOffset44k, sampleCount44k, dest );
			break;
		}
		default: {
			readSamples44k = 0;
			break;
		}
	}

	Sys_LeaveCriticalSection( CRITICAL_SECTION_ONE );

	if ( readSamples44k < sampleCount44k ) {
		memset( dest + readSamples44k, 0, ( sampleCount44k - readSamples44k ) * sizeof( dest[0] ) );
	}
}
Пример #20
0
/*
=====================
idSoundEmitterLocal::StartSound

returns the length of the started sound in msec
=====================
*/
int idSoundEmitterLocal::StartSound( const idSoundShader *shader, const s_channelType channel, float diversity, int soundShaderFlags, bool allowSlow ) {
	int i;

	if ( !shader ) {
		return 0;
	}

	if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
		common->Printf( "StartSound %ims (%i,%i,%s) = ", soundWorld->gameMsec, index, (int)channel, shader->GetName() );
	}

	if ( soundWorld && soundWorld->writeDemo ) {
		soundWorld->writeDemo->WriteInt( DS_SOUND );
		soundWorld->writeDemo->WriteInt( SCMD_START );
		soundWorld->writeDemo->WriteInt( index );

		soundWorld->writeDemo->WriteHashString( shader->GetName() );

		soundWorld->writeDemo->WriteInt( channel );
		soundWorld->writeDemo->WriteFloat( diversity );
		soundWorld->writeDemo->WriteInt( soundShaderFlags );
	}

	// build the channel parameters by taking the shader parms and optionally overriding
	soundShaderParms_t	chanParms;

	chanParms = shader->parms;
	OverrideParms( &chanParms, &this->parms, &chanParms );
	chanParms.soundShaderFlags |= soundShaderFlags;

	if ( chanParms.shakes > 0.0f ) {
		shader->CheckShakesAndOgg();
	}

	// this is the sample time it will be first mixed
	int start44kHz;
	
	if ( soundWorld->fpa[0] ) {
		// if we are recording an AVI demo, don't use hardware time
		start44kHz = soundWorld->lastAVI44kHz + MIXBUFFER_SAMPLES;
	} else {
		start44kHz = soundSystemLocal.GetCurrent44kHzTime() + MIXBUFFER_SAMPLES;
	}

	//
	// pick which sound to play from the shader
	//
	if ( !shader->numEntries ) {
		if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
			common->Printf( "no samples in sound shader\n" );
		}
		return 0;				// no sounds
	}
	int choice;

	// pick a sound from the list based on the passed diversity
	choice = (int)(diversity * shader->numEntries);
	if ( choice < 0 || choice >= shader->numEntries ) {
		choice = 0;
	}

	// bump the choice if the exact sound was just played and we are NO_DUPS
	if ( chanParms.soundShaderFlags & SSF_NO_DUPS ) {
		idSoundSample	*sample;
		if ( shader->leadins[ choice ] ) {
			sample = shader->leadins[ choice ];
		} else {
			sample = shader->entries[ choice ];
		}
		for( i = 0; i < SOUND_MAX_CHANNELS; i++ ) {
			idSoundChannel	*chan = &channels[i];
			if ( chan->leadinSample == sample ) {
				choice = ( choice + 1 ) % shader->numEntries;
				break;
			}
		}
	}

	// PLAY_ONCE sounds will never be restarted while they are running
	if ( chanParms.soundShaderFlags & SSF_PLAY_ONCE ) {
		for( i = 0; i < SOUND_MAX_CHANNELS; i++ ) {
			idSoundChannel	*chan = &channels[i];
			if ( chan->triggerState && chan->soundShader == shader ) {
				if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
					common->Printf( "PLAY_ONCE not restarting\n" );
				}
				return 0;
			}
		}
	}

	// never play the same sound twice with the same starting time, even
	// if they are on different channels
	for( i = 0; i < SOUND_MAX_CHANNELS; i++ ) {
		idSoundChannel	*chan = &channels[i];
		if ( chan->triggerState && chan->soundShader == shader && chan->trigger44kHzTime == start44kHz ) {
			if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
				common->Printf( "already started this frame\n" );
			}
			return 0;
		}
	}

	Sys_EnterCriticalSection();

	// kill any sound that is currently playing on this channel
	if ( channel != SCHANNEL_ANY ) {
		for( i = 0; i < SOUND_MAX_CHANNELS; i++ ) {
			idSoundChannel	*chan = &channels[i];
			if ( chan->triggerState && chan->soundShader && chan->triggerChannel == channel ) {
				if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
					common->Printf( "(override %s)", chan->soundShader->base->GetName() );
				}
				
				chan->Stop();

				// if this was an onDemand sound, purge the sample now
				if ( chan->leadinSample->onDemand ) {
					chan->ALStop();
					chan->leadinSample->PurgeSoundSample();
				}
				break;
			}
		}
	}

	// find a free channel to play the sound on
	idSoundChannel	*chan;
	for( i = 0; i < SOUND_MAX_CHANNELS; i++ ) {
		chan = &channels[i];
		if ( !chan->triggerState ) {
			break;
		}
	}

	if ( i == SOUND_MAX_CHANNELS ) {
		// we couldn't find a channel for it
		Sys_LeaveCriticalSection();
		if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
			common->Printf( "no channels available\n" );
		}
		return 0;
	}

	chan = &channels[i];

	if ( shader->leadins[ choice ] ) {
		chan->leadinSample = shader->leadins[ choice ];
	} else {
		chan->leadinSample = shader->entries[ choice ];
	}

	// if the sample is onDemand (voice mails, etc), load it now
	if ( chan->leadinSample->purged ) {
		int		start = Sys_Milliseconds();
		chan->leadinSample->Load();
		int		end = Sys_Milliseconds();
		session->TimeHitch( end - start );
		// recalculate start44kHz, because loading may have taken a fair amount of time
		if ( !soundWorld->fpa[0] ) {
			start44kHz = soundSystemLocal.GetCurrent44kHzTime() + MIXBUFFER_SAMPLES;
		}
	}

	if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
		common->Printf( "'%s'\n", chan->leadinSample->name.c_str() );
	}

	if ( idSoundSystemLocal::s_skipHelltimeFX.GetBool() ) {
		chan->disallowSlow = true;
	} else {
		chan->disallowSlow = !allowSlow;
	}

	ResetSlowChannel( chan );

	// the sound will start mixing in the next async mix block
	chan->triggered = true;
	chan->openalStreamingOffset = 0;
	chan->trigger44kHzTime = start44kHz;
	chan->parms = chanParms;
	chan->triggerGame44kHzTime = soundWorld->game44kHz;
	chan->soundShader = shader;
	chan->triggerChannel = channel;
	chan->Start();

	// we need to start updating the def and mixing it in
	playing = true;

	// spatialize it immediately, so it will start the next mix block
	// even if that happens before the next PlaceOrigin()
	Spatialize( soundWorld->listenerPos, soundWorld->listenerArea, soundWorld->rw );

	// return length of sound in milliseconds
	int length = chan->leadinSample->LengthIn44kHzSamples();

	if ( chan->leadinSample->objectInfo.nChannels == 2 ) {
		length /= 2;	// stereo samples
	}

	// adjust the start time based on diversity for looping sounds, so they don't all start
	// at the same point
	if ( chan->parms.soundShaderFlags & SSF_LOOPING && !chan->leadinSample->LengthIn44kHzSamples() ) {
		chan->trigger44kHzTime -= diversity * length;
		chan->trigger44kHzTime &= ~7;		// so we don't have to worry about the 22kHz and 11kHz expansions
											// starting in fractional samples
		chan->triggerGame44kHzTime -= diversity * length;
		chan->triggerGame44kHzTime &= ~7;
	}
	
	length *= 1000 / (float)PRIMARYFREQ;

	Sys_LeaveCriticalSection();

	return length;
}