通过DirectSound实时播放PCM+混音

#include "StdAfx.h"
#include "AudioPlayer.h"

#define AUDIO_SECOND_COUNT		1		//10秒缓冲区

CAudioPlayer::CAudioPlayer(void)
{
	
	for (int i = 0; i < AUDIO_MIX_COUNT; ++i)
	{
		m_pDS[i] = NULL;
		m_pDsb[i] = NULL;
		m_pDsb8[i] = NULL;
		m_dwCurPos[i] = 0;
		m_bStop[i] = TRUE;
	}
}

CAudioPlayer::~CAudioPlayer(void)
{
	Uninit();
}

BOOL CAudioPlayer::PlayerInit(DWORD dwChannel, DWORD dwSamplesPerSec, DWORD dwBitsPerSample,HWND wnd, int deviceindex)
{
	/*缓存数据块格式*/	
	m_wfe.wFormatTag = WAVE_FORMAT_PCM;
	m_wfe.nChannels = dwChannel;
	m_wfe.nSamplesPerSec = dwSamplesPerSec;
	m_wfe.wBitsPerSample = dwBitsPerSample;
	m_wfe.nBlockAlign = (dwBitsPerSample/8)*dwChannel;
	m_wfe.nAvgBytesPerSec = dwSamplesPerSec*(dwBitsPerSample/8)*dwChannel;
	m_wfe.cbSize = 0;

/*第一个参数代表应用程序的主窗口,而第二个参数则设定使用资源的优*/
	

	//
	// Create Sound Buffer
	//
	DSBUFFERDESC dsbd;
	memset(&dsbd, 0, sizeof(DSBUFFERDESC));
	dsbd.dwSize = sizeof(DSBUFFERDESC);
	//dsbd.dwBufferBytes = AUDIO_PLAYBUF_LEN;		//40960
	dsbd.dwBufferBytes = m_wfe.nBlockAlign*m_wfe.nAvgBytesPerSec;
	dsbd.lpwfxFormat = (LPWAVEFORMATEX) (&m_wfe);
	//其中GLOBALFOCUS标志位保证当程序窗口不在前面时仍能够播放
	/*Pan(左右正道的差值)*/
// 	DSBCAPS_CTRL3D   //缓冲区具有的3D音效控制能力,不能与DSBCAPS_CTRLPAN一起使用
// 		DSBCAPS_CTRLFREQUENCY   //可设置采样频率
// 		DSBCAPS_CTRLFX  //缓冲区支持特效处理,但缓冲区必须够大,可容纳更多的数据
// 		DSBCAPS_CTRLPAN  //缓冲区可以控制声道
// 		DSBCAPS_CTRLPOSITIONNOTIFY  //缓冲区具有播放位置通知能力
// 		DSBCAPS_CTRLVOLUME  //缓冲区可设置音量大小
// 		DSBCAPS_GLOBALFOCUS  //缓冲是一个全局声音资源,当前程序切换到其他程序依然可以继续播放.
// 		DSBCAPS_LOCDEFER  //缓冲区可绑定硬件内存或者软件内存来播放声音
// 		DSBCAPS_LOCHARDWARE  //缓冲区必须使用硬件的混声器,如果不支持硬件内存或者混声器,都会导致创建缓冲区失败.
// 		DSBCAPS_LOCSOFTWARE  //缓冲区使用软件内存或者使用软件混音
// 		DSBCAPS_MUTE3DATMAXDISTANCE  //超过声音可听的最大距离,将停止播放声音
// 		DSBCAPS_PRIMARYBUFFER  //说明缓冲区的为主缓冲区(如果没说明,则用作次缓冲区)
// 		DSBCAPS_STATIC  //自动使用硬件内存做缓冲区
// 		DSBCAPS_STICKYFOCUS  //当程序切换到其他不使用DIRECTSOUND 的程序时,缓冲区继续播放声音,但无法如常进行其他处理
	//dsbd.dwFlags =  DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_STATIC | DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
	dsbd.dwFlags =  DSBCAPS_LOCSOFTWARE  | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME;
	
	

/*在设置协调层级时,标志位必须设定为DSSCL_PRIORITY.
次缓冲区则存储播放声音的文件。在加载声音文件后,
只要调用Play()方法,声音就会自动的送入主缓冲区中并进行播放*/
	
	HRESULT result = 0;

	AdoAudioDevices adoad;
	int ncount = adoad.getDevCount(AdoAudioDevices::audio_render);

	for (int i = 0; i < AUDIO_MIX_COUNT; ++i)
	{			
		result = DirectSoundCreate(&adoad.sysAudioDevIndex2DevGuid(deviceindex,AdoAudioDevices::audio_render), &m_pDS[i], NULL);
		if(result != DS_OK)
		{	
			return FALSE;
		}
		result = m_pDS[i]->SetCooperativeLevel(wnd, DSSCL_PRIORITY);
		if(result != DS_OK)
		{
			return FALSE;
		}
		/*建立次缓冲区*/
		/*每一个声音对应一个辅助缓冲区,可以有多个辅助缓冲区*/
		if( FAILED(m_pDS[i]->CreateSoundBuffer(&dsbd,&m_pDsb[i],NULL))){     
			return FALSE;  
		} 			
	}
	return TRUE;
}

BOOL CAudioPlayer::Start(int nCase)
{

/*
播放声音要通过以下步骤:
1、锁定辅助缓冲的一部分以获得你所需要的那部分缓冲的基址。
2、向缓冲写数据。
3、解锁。
4、使用IDirectSoundBuffer::Play方法来播放声音。
如果是使用的流缓冲,还需要反复的执行1-3步骤。*/

	LPDIRECTSOUNDBUFFER pDsb;
	if ( ( nCase>=0 ) && ( nCaseGetStatus(&dwStatus))
	{		
		return FALSE;
	}
	if((dwStatus & DSBSTATUS_PLAYING) != DSBSTATUS_PLAYING)
	{
		/**/
		pDsb->SetCurrentPosition(0);

		hr = pDsb->Play(0, 0,0);//DSBPLAY_LOOPING); 
		//hr = pDsb->Play(0, 0, 0);

		if (DS_OK != hr)
		{
			m_bStop[nCase] = TRUE;
			return FALSE;
		}
		m_bStop[nCase] = FALSE;
	}
	return TRUE;
}

BOOL CAudioPlayer::Pause(int nCase)
{
	LPDIRECTSOUNDBUFFER pDsb;
	if (m_bStop[nCase])
	{
		return FALSE;
	}

	if ( ( nCase>=0 ) && ( nCaseStop();

	if (rs != DS_OK)
	{
		Sleep(1);
		pDsb->Stop();
	}
	FillBlankToBuf(0, nCase);
	m_bStop[nCase] = TRUE;
	return TRUE;
}

BOOL CAudioPlayer::Stop(int nCase)
{
	if (m_bStop[nCase])
	{
		return FALSE;
	}
	LPDIRECTSOUNDBUFFER pDsb;

	if( (nCase>=0) && (nCaseGetStatus(&dwStatus))
	{
		return FALSE;
	}

	if ((dwStatus & DSBSTATUS_PLAYING) == DSBSTATUS_PLAYING)
	{  
		MMRESULT rs;
		rs = pDsb->Stop();
		if(rs != DS_OK)
		{
			Sleep(1);
			pDsb->Stop();
		}
		FillBlankToBuf(0, nCase);
		m_bStop[nCase] = TRUE;
	}
	return TRUE;
}



BOOL CAudioPlayer::Uninit(void)
{
	for( int i = 0 ; i < AUDIO_MIX_COUNT ; i++ )
	{
		Stop(i);
	}
	return TRUE;
}


void CAudioPlayer::SetVolume(long nNewValue)
{
	nNewValue *= -1;
	if ( nNewValue < DSBVOLUME_MIN )
	{
		nNewValue = DSBVOLUME_MIN ;
	}
	if ( nNewValue > DSBVOLUME_MAX )
	{
		nNewValue = DSBVOLUME_MAX ;
	}
	for ( int i = 0; i < AUDIO_MIX_COUNT; i++ )
	{
		m_pDsb[i]->SetVolume(nNewValue);
	}
}

long CAudioPlayer::GetVolume(void)
{
	long vol;
	m_pDsb[0]->GetVolume(&vol);
	return vol;
}


BOOL CAudioPlayer::FillBlankToBuf(ULONG pos , int nCase) // nCase 0基
{
	DWORD len = m_wfe.nAvgBytesPerSec;

	LPVOID lpvPtr1;
	DWORD dwBytes1;
	LPVOID lpvPtr2;
	DWORD dwBytes2;
	HRESULT hr;

	LPDIRECTSOUNDBUFFER pDsb;
	if( (nCase>=0) && (nCaseLock(pos, len, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
	if (DSERR_BUFFERLOST == hr)
	{
		pDsb->Restore();
		hr = pDsb->Lock(pos, len, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
	}
	if (DS_OK == hr)
	{
		ZeroMemory(lpvPtr1, dwBytes1);
		if (NULL != lpvPtr2)
		{
			ZeroMemory(lpvPtr2, dwBytes2);
		}
		hr=pDsb->Unlock(lpvPtr1, dwBytes1, lpvPtr2, dwBytes2);
		return TRUE;
	}

	pDsb->SetCurrentPosition(0);

	return TRUE;
}


BOOL CAudioPlayer::FillBuffer(unsigned char *pdata, DWORD len, int nCase) // nCase 0基
{
	LPVOID lpvPtr1;
	DWORD dwBytes1;
	LPVOID lpvPtr2;
	DWORD dwBytes2;
	HRESULT hr;

/* 首先要建立一个代表声卡的DirectSound对象*/
	LPDIRECTSOUNDBUFFER pDsb;
	DWORD curpos;
	if( ( nCase >= 0 ) && ( nCase < AUDIO_MIX_COUNT ) )
	{
		pDsb = m_pDsb[nCase];
		curpos = m_dwCurPos[nCase];
	}
	else
	{
		return FALSE;
	}

	// *********** ptr modify ***********
	DWORD wp1 , wp2 ;
	pDsb->GetCurrentPosition(&wp1, &wp2);
	//TRACE("[%d]",curpos);
	//DWORD dwCount = 0;	
	// *********** end ptr modify *******		
	pDsb->SetCurrentPosition(0);	
	hr = pDsb->Lock(wp2, len, &lpvPtr1, &dwBytes1,&lpvPtr2, &dwBytes2, DSBLOCK_FROMWRITECURSOR );
	if (DSERR_BUFFERLOST == hr)
	{		
		pDsb->Restore();		
		hr = pDsb->Lock(wp2, len, &lpvPtr1, &dwBytes1,&lpvPtr2, &dwBytes2, DSBLOCK_FROMWRITECURSOR );
	}
	if (DS_OK == hr)
	{
		CopyMemory(lpvPtr1, pdata, len); 
		if (NULL != lpvPtr2) 
		{
			CopyMemory(lpvPtr2, pdata, len); 
		} 		
		hr = pDsb->Unlock(lpvPtr1, dwBytes1,lpvPtr2, dwBytes2);
		if (hr != DS_OK)
		{
			return FALSE;
		}			
		return TRUE;
	}	
	pDsb->Restore();	
	pDsb->SetCurrentPosition(0);
	return FALSE;
}

你可能感兴趣的:(C++,DX)