⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sound.cpp

📁 hl2 source code. Do not use it illegal.
💻 CPP
📖 第 1 页 / 共 3 页
字号:
	}
}

void CFacePoserSound::PlayPartialSound( StudioModel *model, const char *wavfile, CAudioMixer **ppMixer, int startSample, int endSample )
{
	if ( !m_pAudio )
		return;

	StopAll();
	CAudioSource *wave = FindOrAddSound( wavfile );
	if ( !wave )
		return;

	CAudioMixer *mixer = wave->CreateMixer();
	if ( ppMixer )
	{
		*ppMixer = mixer;
	}
	
	m_pAudio->AddSource( mixer );
	mixer->SetSamplePosition( startSample );
	mixer->SetLoopPosition( endSample );
}

void CFacePoserSound::PlaySound( CAudioSource *source, CAudioMixer **ppMixer )
{
	if ( ppMixer )
	{
		*ppMixer = NULL;
	}

	if ( m_pAudio )
	{
		CAudioMixer *mixer = source->CreateMixer();
		if ( ppMixer )
		{
			*ppMixer = mixer;
		}
		m_pAudio->AddSource( mixer );
	}
}

enum
{
	PHONEME_CLASS_WEAK = 0,
	PHONEME_CLASS_NORMAL,
	PHONEME_CLASS_STRONG,

	NUM_PHONEME_CLASSES
};

struct Emphasized_Phoneme
{
	char			*classname;
	bool			required;
	bool			valid;
	CExpClass		*cl;
	CExpression		*exp;
	float			*settings;
	float			amount;
};

static Emphasized_Phoneme g_PhonemeClasses[ NUM_PHONEME_CLASSES ] =
{
	{ "phonemes_weak",		false },
	{ "phonemes",			true },
	{ "phonemes_strong",	false },
};

#define STRONG_CROSSFADE_START		0.60f
#define WEAK_CROSSFADE_START		0.40f

void ComputeBlendedSetting( Emphasized_Phoneme *classes, float emphasis_intensity )
{
	// Here's the formula
	// 0.5 is neutral 100 % of the default setting

	// Crossfade starts at STRONG_CROSSFADE_START and is full at STRONG_CROSSFADE_END
	// If there isn't a strong then the intensity of the underlying phoneme is fixed at 2 x STRONG_CROSSFADE_START
	//  so we don't get huge numbers

	bool has_weak = classes[ PHONEME_CLASS_WEAK ].valid;
	bool has_strong = classes[ PHONEME_CLASS_STRONG ].valid;

	Assert( classes[ PHONEME_CLASS_NORMAL ].valid );

	if ( emphasis_intensity > STRONG_CROSSFADE_START )
	{
		if ( has_strong )
		{
			// Blend in some of strong
			float dist_remaining = 1.0f - emphasis_intensity;
			float frac = dist_remaining / ( 1.0f - STRONG_CROSSFADE_START );

			classes[ PHONEME_CLASS_NORMAL ].amount = (frac) * 2.0f * STRONG_CROSSFADE_START;
			classes[ PHONEME_CLASS_STRONG ].amount = 1.0f - frac; 
		}
		else
		{
			emphasis_intensity = min( emphasis_intensity, STRONG_CROSSFADE_START );
			classes[ PHONEME_CLASS_NORMAL ].amount = 2.0f * emphasis_intensity;
		}
	}
	else if ( emphasis_intensity < WEAK_CROSSFADE_START )
	{
		if ( has_weak )
		{
			// Blend in some weak
			float dist_remaining = WEAK_CROSSFADE_START - emphasis_intensity;
			float frac = dist_remaining / ( WEAK_CROSSFADE_START );

			classes[ PHONEME_CLASS_NORMAL ].amount = (1.0f - frac) * 2.0f * WEAK_CROSSFADE_START;
			classes[ PHONEME_CLASS_WEAK ].amount = frac; 
		}
		else
		{
			emphasis_intensity = max( emphasis_intensity, WEAK_CROSSFADE_START );
			classes[ PHONEME_CLASS_NORMAL ].amount = 2.0f * emphasis_intensity;
		}
	}
	else
	{
		classes[ PHONEME_CLASS_NORMAL ].amount = 2.0f * emphasis_intensity;
	}
}

void CFacePoserSound::AddViseme( float intensity, StudioModel *model, int phoneme, float scale )
{
	int i;

	Assert( model );
	studiohdr_t *hdr = model->getStudioHeader ();
	Assert( hdr );
	if ( !hdr )
		return;

	for ( i = 0; i < NUM_PHONEME_CLASSES; i++ )
	{
		Emphasized_Phoneme *info = &g_PhonemeClasses[ i ];
		
		info->valid = false;
		info->exp = NULL;
		info->settings = NULL;
		info->amount = 0.0f;
		
		info->cl = expressions->FindClass( info->classname );
		if ( info->cl )
		{
			info->exp = info->cl->FindExpression( ConvertPhoneme( phoneme ) );
		}

		if ( info->required && ( !info->cl || !info->exp ) )
		{
			return;
		}

		if ( info->exp )
		{
			info->valid = true;

			// Resolve markov chains
			info->exp = info->exp->GetExpression();

			// Allow overrides
			if ( FacePoser_GetOverridesShowing() )
			{
				if ( info->exp->HasOverride() && info->exp->GetOverride() )
				{
					info->exp = info->exp->GetOverride();
				}
			}

			info->settings = info->exp->GetSettings();
			Assert( info->settings );
		}
	}

	ComputeBlendedSetting( g_PhonemeClasses, intensity );

	// Look up the phoneme
	for ( i = 0; i < hdr->numflexcontrollers; i++)
	{
		int j = hdr->pFlexcontroller( i )->link;

		float add = 0.0f;

		for ( int k = 0 ; k < NUM_PHONEME_CLASSES; k++ )
		{
			Emphasized_Phoneme *info = &g_PhonemeClasses[ k ];
			if ( !info->valid || !info->amount )
				continue;

			add += info->amount * info->settings[ j ];
		}

		if ( add == 0.0f )
			continue;

		float curvalue = model->GetFlexController( i );
		curvalue += add * scale;
		model->SetFlexController( i, curvalue );
	}
}


#define PHONEME_FILTER 0.08f
#define PHONEME_DELAY  0.0f

void CFacePoserSound::SetupWeights( void )
{
	StudioModel *model;
	int c = models->Count();
	for ( int i = 0; i < c; i++ )
	{
		model = models->GetStudioModel( i );
		if ( !model )
			continue;

		// Reset flexes
		studiohdr_t *hdr = model->getStudioHeader();
		if ( !hdr )
			continue;

		for ( int s = 0; s < model->m_mouth.GetNumVoiceSources(); s++ )
		{
			CVoiceData	*vd = model->m_mouth.GetVoiceSource( s );
			if ( !vd )
				continue;

			CAudioSource *source = vd->m_pAudioSource;
			// check for phoneme flexes
			if ( !source )
				continue;

			CAudioMixer *mixer = FindMixer( source );
			if ( !mixer )
				continue;

			CSentence *sentence = source->GetSentence();
			if ( !sentence )
				continue;

			// Zero faces if needed
			models->CheckResetFlexes();

			float pos = (float)mixer->GetSamplePosition();

			// Con_Printf( "pos %f for mixer %p\n", pos, mixer );

			float	soundtime = pos / source->SampleRate();

			float	t = soundtime - PHONEME_DELAY;
			float	dt = PHONEME_FILTER;

			float	sentence_duration = source->GetRunningLength();
			float	emphasis_intensity = sentence->GetIntensity( t, sentence_duration );

			if ( t > 0.0f )
			{
				for ( int w = 0 ; w < sentence->m_Words.Size(); w++ )
				{
					CWordTag *word = sentence->m_Words[ w ];
					if ( !word )
						continue;

					for ( int k = 0; k < word->m_Phonemes.Size(); k++)
					{
						CPhonemeTag *phoneme = word->m_Phonemes[ k ];
						if ( !phoneme )
							continue;

						// if the filter starts within this phoneme, make sure the filter size is 
						// at least least as long as the current phoneme, or until the end of the next phoneme, 
						// whichever is smaller
						if (t > phoneme->m_flStartTime && t < phoneme->m_flEndTime)
						{
							if (k < word->m_Phonemes.Size()-1)
							{
								dt = max( dt, min( word->m_Phonemes[ k+1 ]->m_flEndTime - t, phoneme->m_flEndTime - phoneme->m_flStartTime ) );
							}
						}

						float t1 = ( phoneme->m_flStartTime - t) / dt;
						float t2 = ( phoneme->m_flEndTime - t) / dt;

						if (t1 < 1.0 && t2 > 0)
						{
							float scale;

							// clamp
							if (t2 > 1)
								t2 = 1;
							if (t1 < 0)
								t1 = 0;

							// FIXME: simple box filter.  Should use something fancier
							scale = (t2 - t1);

							AddViseme( emphasis_intensity, model, phoneme->m_nPhonemeCode, scale );
						}
					}
				}

				ProcessCloseCaptionData( model, t, sentence );
			}
		}
	}
}

static int g_nSoundFrameCount = 0;

void CFacePoserSound::ProcessCloseCaptionData( StudioModel *model, float curtime, CSentence* sentence )
{
	closecaptionmanager->Process( g_nSoundFrameCount, model, curtime, sentence );
}

void CFacePoserSound::Update( float dt )
{
	closecaptionmanager->PreProcess( g_nSoundFrameCount );

	if ( m_pAudio )
	{
		SetupWeights();
		m_pAudio->Update( m_flElapsedTime );
	}

	closecaptionmanager->PostProcess( g_nSoundFrameCount, dt );

	m_flElapsedTime += dt;
	g_nSoundFrameCount++;
}

void CFacePoserSound::Flush( void )
{
	if ( m_pAudio )
	{
		m_pAudio->Flush();
	}
}

void CFacePoserSound::StopAll( void )
{
	int c = models->Count();
	for ( int i = 0; i < c; i++ )
	{
		StudioModel *model = models->GetStudioModel( i );
		if ( model )
		{
			model->m_mouth.ClearVoiceSources();
		}
	}

	if ( m_pAudio )
	{
		m_pAudio->StopSounds();
	}
}

void CFacePoserSound::StopSound( CAudioMixer *mixer )
{
	int idx = m_pAudio->FindSourceIndex( mixer );
	if ( idx != -1 )
	{
		m_pAudio->FreeChannel( idx );
	}
}

void CFacePoserSound::RenderWavToDC( HDC dc, RECT& outrect, COLORREF clr, 
	float starttime, float endtime, CAudioSource *pWave, 
	bool selected /*= false*/, int selectionstart /*= 0*/, int selectionend /*= 0*/ )
{
	channel_t channel;

	channel.leftvol = 200;
	channel.rightvol = 200;
	channel.pitch = 1.0;

	if ( !pWave )
		return;

	CAudioWaveOutput *pWaveOutput = ( CAudioWaveOutput * )m_pAudio;

	CAudioMixer *pMixer = pWave->CreateMixer();

	float timeperpixel = ( endtime - starttime ) / (float)( outrect.right - outrect.left );

	float samplesperpixel = timeperpixel * pWave->SampleRate();

	samplesperpixel = min( samplesperpixel, (float)PAINTBUFFER_SIZE );

	int intsamplesperpixel = (int)samplesperpixel;

	// Determine start/stop positions
	int totalsamples = (int)( pWave->GetRunningLength() * pWave->SampleRate() );

	if ( totalsamples <= 0 )
		return;

	float selectionstarttime = pWave->GetRunningLength() * ( float )selectionstart  / ( float )totalsamples;
	float selectionendtime = pWave->GetRunningLength() * ( float )selectionend  / ( float )totalsamples;


	HPEN oldPen, pen, pen2, pen3, pen4;

	pen = CreatePen( PS_SOLID, 1, RGB( 175, 175, 250 ) );
	pen2 = CreatePen( PS_SOLID, 1, clr );
	pen3 = CreatePen( PS_SOLID, 1, RGB( 127, 200, 249 ) );
	pen4 = CreatePen( PS_SOLID, 2, RGB( 0, 0, 200 ) );

	oldPen = (HPEN)SelectObject( dc, pen );

	MoveToEx( dc, outrect.left, ( outrect.bottom + outrect.top ) / 2, NULL );
	LineTo( dc, outrect.right, ( outrect.bottom + outrect.top ) / 2 );

	SelectObject( dc, pen2 );

	// Now iterate the samples
	float currenttime = 0.0f;
	int pixel = 0;
	int height = ( outrect.bottom - outrect.top ) / 2;
	int midy = ( outrect.bottom + outrect.top ) / 2;
	int bufferlen = ( intsamplesperpixel + 3 ) & ~3;
	short *samples = new short[ 2 * bufferlen ];
	bool drawingselection = false;
	int maxsamples = max( 32, intsamplesperpixel / 16 );
	int currentsample = 0;

	while ( currenttime < endtime )
	{

		pWaveOutput->m_audioDevice.MixBegin();

		int samplecount = min( maxsamples, intsamplesperpixel );

		if ( !pMixer->MixDataToDevice( &pWaveOutput->m_audioDevice, &channel, currentsample, samplecount, pWave->SampleRate(), true ) )
			break;

		currentsample = pMixer->GetSamplePosition();

		// Jump ahead by diff
		int diff = intsamplesperpixel - samplecount;
		if ( diff > 0 )
		{
			if ( !pMixer->SkipSamples( &channel, currentsample, diff, pWave->SampleRate(), true ) )
				break;
		}

		currentsample = pMixer->GetSamplePosition();

		pWaveOutput->m_audioDevice.TransferBufferStereo16( samples, samplecount );

		if ( currenttime >= starttime )
		{
			if ( selected )
			{
				bool boundary = false;
				bool inselection = ( currenttime >= selectionstarttime && 
					currenttime <= selectionendtime );

				if ( inselection )
				{
					if ( !drawingselection )
					{
						drawingselection = true;
						boundary = true;
					}
				}
				else if ( drawingselection )
				{
					boundary = true;
					drawingselection = false;
				}

				if ( inselection || boundary )
				{
					int top, bottom;

					bottom = outrect.bottom;

					HPEN *usePen;
					if ( boundary )
					{
						usePen = &pen4;
						top = outrect.top;
					}
					else
					{
						usePen = &pen3;
						top = outrect.bottom - 19;
					}

					HPEN old = (HPEN)SelectObject( dc, *usePen );
		
					MoveToEx( dc, outrect.left + pixel, top, NULL );
					LineTo( dc, outrect.left + pixel, bottom-1 );

					SelectObject( dc, old );
				}
			}


			int maxvalue = -65536;
			int minvalue = 65536;
			
			short *pData = samples;

			// only take fix samples
			int step = 2;
			int count = 2 * samplecount;

			for ( int i = 0; i < count; i+=step )
			{
				int val = (int)( pData[i] + pData[i+1] ) / 2;

				if ( val > maxvalue )
				{
					maxvalue = val;
				}
				
				if ( val < minvalue )
				{
					minvalue = val;
				}
			}

			float maxv = (float)( maxvalue ) / 32768.0f;
			float minv = (float)( minvalue ) / 32768.0f;

			MoveToEx( dc, outrect.left + pixel, midy + ( int ) ( maxv * height ), NULL );
			LineTo( dc, outrect.left + pixel, midy + ( int ) ( minv * height ) );

			pixel++;
		}
		currenttime += timeperpixel;
	}

	delete[] samples;

	SelectObject( dc, oldPen );
	DeleteObject( pen );
	DeleteObject( pen2 );
	DeleteObject( pen3 );

	delete pMixer;
}

bool CFacePoserSound::IsSoundPlaying( CAudioMixer *pMixer )
{
	if ( !m_pAudio || !pMixer )
	{
		return false;
	}

	//
	int index = m_pAudio->FindSourceIndex( pMixer );
	if ( index != -1 )
		return true;

	return false;
}

CAudioMixer *CFacePoserSound::FindMixer( CAudioSource *source )
{
	if ( !m_pAudio )
		return NULL;

	return m_pAudio->GetMixerForSource( source );
}


void CFacePoserSound::EnsureNoModelReferences( CAudioSource *source )
{
	int c = models->Count();
	for ( int i = 0; i < c; i++ )
	{
		StudioModel *model = models->GetStudioModel( i );
		if ( model->m_mouth.IsSourceReferenced( source ) )
		{
			model->m_mouth.RemoveSource( source );
		}
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -