#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 ) && ( nCase
GetStatus(&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 ) && ( nCase
Stop();
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) && (nCase
GetStatus(&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) && (nCase
Lock(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;
}
通过DirectSound实时播放PCM+混音
最新推荐文章于 2024-07-19 14:05:38 发布
本文介绍了一个基于DirectSound API的音频播放器类的实现细节。该类支持初始化、开始、暂停、停止音频播放等功能,并提供了调整音量的方法。文章深入讲解了如何创建并管理音频缓冲区,以及如何通过锁定位、写入数据、解锁等步骤来播放音频。
摘要由CSDN通过智能技术生成