真正的音频高手们,一个很专业的问题

时间:2021-05-17 03:44:43
同时接收多路声音并播放,即一个终端接收多路声音,混音播放问题。可是音频输出设备只有一个,怎样做才能同时播放多路声音?

74 个解决方案

#1


高个p.用dsound

#2


混音的实现代码以前贴子有的,你搜一下

#3


dsound编程有点头疼,请高手指点。

混音的实现原理是什么,偶一点都不知道,请高手也指点。

#4


如果连 dsound都没信心去学会的话,就不要混了,回家吧

#5


因为我输出单路时用dsound没有用waveOut的效果好,所以我没有用dsound,而且用dsound有个毛病:别的窗口将该窗口覆盖时都放不出声音了,非要让播放窗口激活,而且机器不锁定以后甚至屏保后也放不出声音来。

#6


wyly() ,你说话能不能尊重别人一点,至少我还是一个重点大学的高才硕士生!

#7


啊?~~

#8


呵呵,同意。再高的人也是从菜鸟过来的……

我用DX8做过音频,背景音乐是MIDI,但是其实是用DLS合成后送到主缓冲的。切换出来以后音乐也没有停止。估计要是放WAV也不会有什么问题吧?当然我的协作级别设的很高。你也设的高一点试试,或许不会停止。

#9


呵呵,开玩笑。心情不好

#10


谢谢finalvictory(维克) !

应该在哪个地方设置级别?是在SetCooperativeLevel()中吗?我在其中都设置过了,各种都试过,还是没用呀!而且,怎么用dsound对声音进行混音呀?

#11


混音过程在内部完成,用不着你操心的,你只要调用PlaySegment或者PlaySegmentEx就可以播放了,所有的声音会按照你制定的音量大小和平衡配比正确地播放出来的。

当然了,这是DX8的做法,如果你没有什么底层操作的话,就用DX8吧。因为它比前几个版本的DSound还有DMusic要简单的多了,起码不用直接访问声音缓冲了。

DX8中的IDirectMusicPerformance初始化可以在InitAudio()中一步完成,当然也可以让它返回DSound和DMusic的接口,如果想精细操作的话。我就是一步完成的,属于很经典的那一种模式,你可以看看DirectMusic的例子,都可以失去焦点以后继续播放的,但是DirectSound的例子就不行了。

DX8的音频部分学起来很简单的,我只一个晚上就能应付一般的音频播放情况了,你也没问题的。

#12


finalvictory(维克),可是我是要进行底层操作呀。
我要接收从网络上发过来的音频数据,然后解码播放呀,那应该怎么做呀?

#13


如果你能够解码的话,只要把每个数据流格式化成为一个标准的DSound缓冲,然后播放就可以了,剩下的事情交给DSound。

#14


我给你留言了,去看看吧。

#15


我看到过一段源代码,是在dll中实现同时播放wav的函数,最多可以拨四路,每一路都可以单独控制播放和停止。

#16


我用dsound播放从网络中传来的音频数据时,播放完了好象还有回音,哪位高手能告诉我这是怎么回事吗?

#17


我用dsound播放从网络中传来的数据好象播放完后有回音,哪位高手能告诉我这是怎么回事吗?

#18


#19


#20


#21


高手,用dsound怎样实现混音?
假设我将各路声音都送到一个二级缓冲中去,再播放可以自动实现混音吗?

#22


可以的。要不然还叫DSound吗?

呵呵,又回来了。

#23


谢谢finalvictory!
可是我还是不明白怎样把多路接收过来的声音放到二级缓冲,假设接收的声音解码后的数据为p1,p2,p3...,pn,我是不是要有n个二级缓冲pBuf1,pBuf2,pBuf3,...,pBufn?把数据分别装进去后,怎样跟主缓冲联系起来?

#24


我来说说混音的底层实现原理吧
1  声音是一种波形流,而在计算机中却需要用数字表示。所以在这一过程中
   有一个量化的概念
2  量化过程涉及到几个概念。取样频率、速率及位数
3  那混音是怎样实现的呢?
    声音是一种波,它遵循波的叠加原理。多个声音合成在一起也即就是波
    的叠加后的结果。所以我们混音的步骤就是:
    (1)  首先须把所有待合成的声音统一成一种格式
    (2)  把数据相加即可,超进最大值时,置为最大值。
    数据值是对应波形中的音量,数据相加即音量相加,也即为波的叠加
4  WaveOutXXX(...)这类函数很容易出错,所以还是建议使用dsound

#25


你的“声音解码后的数据”是什么意思?p1,p2,p3,...,pn是指每一个数据采样还是每一路数据采样的头指针?

我想你的意思是后者,那么你要做的就是建立n个流式缓冲区。在初始化阶段,用DSBPLAY_LOOPING作为参数调用每个缓冲区的Play(),这样就开始了监听过程。当接收到了数据并且解码了以后,把声音缓冲Lock()住,然后用你最快的速度把解码后的数据填入Lock()返回的写指针,然后调用Unlock()。当缓冲区内部的读指针指向你的这块数据的时候,你的这段声音自然就会被播放出来了。具体混音过程由DSound实现了。

#26


to freelybird(阿愚):

声音相加可以吗?好像没有这么简单吧?我在Dos下做过支持混音播放的声卡驱动程序,效果很不理想。其原理是在两次中断之间把各路声音混合到后备缓冲区中。我发现如果用简单相加的话,很容易就达到极值了,声音会是一片滋滋啦啦。好像必须把每一路声音都减小一些才行吧,因为我看过allegro的文档,上面说初始化的时候声音通道数不要设得太大,否则声音减小得很厉害,所以就有了上面的想法。

#27


我也觉得waveOut不稳定,所以想用dsound。
可是Lock的时候同时Lock吗?我写了个用dsound进行声音输出的类CDirectSound,里面有个函数是PlayAudio(unsigned char *pBuf,int len)参数当然是解码后的声音数据何声音数据长度,这个类可以实现一路声音的连续播放。我要实现混音,我应该创建几个CDirectSound对象?怎么调用PlayAudio?

#28


你的封装级别太高了!你是不是把整个声音播放过程封装起来了?这样不行的!你应该只封装一个IDirectSoundBuffer*对象。而且要保证你封装的所有对象都是从一个IDirectSound*对象创建的。建议你顺着DSound的结构来,这一点从微软给出的例子程序中也可以看出来。也就是说最好有一个CSoundManager之类的东东负责创建、维护和管理你所有的CDirectSound对象。一个CDirectSound对象对应着一个IDirectSoundBuffer*对象,也即对应着一路声音。

Lock()在相同的IDirectSoundBuffer*对象上面不能嵌套调用,可是在不同的此类对象上面随便用,因为它们管理者不同的声音缓冲区,相互之间不会冲突。所以说如果你把整个声音播放过程封装起来是不能实现多路混音播放的。假设你按照上面说的方法封装好了所有的东西的话,整个过程就要简单的多了:

1、建立一个CDirectSoundManager对象然后初始化DirectSound;
2、根据要接收的声音的路数创建相应多个CDirectSound对象;
3、开辟另外一个线程用轮询的方式来播放流式缓冲区,比如:

EnterCriticalSection(&g_csLocalSoundBuffer);
for ( i = 0 ; i < N ; i++ )
{
  if ( dwSoundLengths[i] )
    pDSound[i] -> PlayAudio(ppSounds[i], dwSoundLengths[i]);
}
LeaveCriticalSection(&g_csLocalSoundBuffer);

4、在主线程中接收网络上来的消息,然后填充ppSounds和dwSoundLengths。

#29


收藏了

#30


if((hr = DirectSoundCreate(NULL,&m_pDirectObject,NULL))==DS_OK)
{
//设定应用程序的声音设备优先级别方式,一般为DSSCL_NORMAL,DSSCL_PRIORITY
 hr = m_pDirectObject->SetCooperativeLevel(AfxGetMainWnd()->m_hWnd,//GetDesktopWindow(),//
DSSCL_PRIORITY );  
 CreateBuffer();
 return true;
}
    


int CDirectSound::PlayAudio(unsigned char *p, int Len)
{
if(Len<=0)
return -1;

if ( LoadData(m_pDSoundBuffer[iPlay],plen) )///Lock数据
m_pDSoundBuffer[iPlay]->Play (0, 0,0) ;

iPlay = (iPlay+1) % MAX_BUF_NUM;

return iPlay;
}

请问我哪些地方可以公用哪些地方要分别创建?

#31


finalvictory(维克) ,那我的封装只需要把除IDirectSound*对象以外的地方封装到都行了,是吗?

#32


我看了你的程序,iPlay是什么,是一个全局变量吗?这样的话你是不是想用一个CSoundBuffer对象搞定所有路的声音?每调用一次PlayAudio(),iPlay就加一以便可以下一次向下一个缓冲区中填写数据?这是你的意思吗?这样是不行的!我分析如下:

声音当然是按照被采集的顺序发送,可是由于网络原因,到达的顺序是不确定的!像你这样处理的话,极有可能把张三的声音送到李四的缓冲区里面去的!这不就乱套了?

另外,你好象还是没有明白我的意思。我是说用一个CSoundBuffer控制一路声音,而不是所有的声音。这样程序结构会清楚许多,自然就好编得多了。

#33


这几天有事情没能来。

 finalvictory(维克),我的iPlay不是你所说的那样,因为一路声音要一个缓冲区播放的话声音是不连续的,所以我采用几个缓冲区的方法来播放使声音连贯。

#34


看不懂,dos下怎么搞呢?

#35


我不管dos下怎么搞。

#36


你的程序写完了吗?我这两天没事儿的时候也在琢磨你这个问题,过些时候我把我的代码贴出来给你看看。

#37


finalvictory(维克) ,太谢谢你了。
如果您有兴趣的话,我可以把我的代码发给您,看我那些地方有问题。

#38


如果2路音频,那就用waveOutOpen打开2个音频设备,然后同时调用waveOutWirte写入音频数据,这样播放时声音是同时播放的。

以上想法我已做过测试,在WINDOWS 2000/XP上均有效,98上不知道行不行,与混音算法相比,这可能是最容易的方法了。

#39


zwok() ,可是哪有两个音频设备呀!一般一台机器只有一个音频设备呀。

#40


你有没有试过同一个音频设备调用2次waveOutOpen呢?

我在win2k/XP上均测试成功

#41


在同一个音频设备上调用2次waveOutOpen,然后会得到2个handle,然后同时向2个HANDLE中写数据,这时是同时播放的,经测试效果不错。

在win2k/xp上均测试成功

#42


嗯,我搞定了,用DX8。有两个类:

CSound:负责维护IDirectSoundBuffer8对象。
CSoundManager:负责维护CSound对象。

下面把核心类的代码给你粘出来:

这是CSound的头文件sound.h:

#if !defined SOUND_H__
#define SOUND_H__

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

class CSound
{
friend class CSoundManager;
// Protected data members
protected:
LPDIRECTSOUNDBUFFER8 m_pDSBuffer;
WAVEFORMATEX m_wfx;
DWORD m_dwBufferSize;
DWORD m_dwWritePos;
BYTE m_btSilence;

// Protected constructor and destructor
protected:
CSound();
virtual ~CSound();

// Internal functions
protected:
HRESULT Create(LPDIRECTSOUND8 pDS, LPWAVEFORMATEX lpwfxFormat);
LONG ReceiveData(LPVOID pData, DWORD dwDataSize);
HRESULT Start();
HRESULT Stop();
BOOL IsPlaying();
};

#endif

#43


这是CSound的原文件sound.cpp:

#include "stdafx.h"
#include "Sound.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#if !defined RELEASENULL
#define RELEASENULL(p) { if(p) { (p) -> Release(); (p) = NULL; } }
#endif

#define SOUND_BUFFER_SIZE 1 // in seconds

CSound::CSound()
{
m_pDSBuffer = NULL;
m_dwWritePos = 0;
}

CSound::~CSound()
{
RELEASENULL(m_pDSBuffer);
}

HRESULT CSound::Create(LPDIRECTSOUND8 pDS, LPWAVEFORMATEX lpwfxFormat)
{
LPDIRECTSOUNDBUFFER pDSBuffer;
LPDIRECTSOUNDBUFFER8 pDSBuffer8;
DSBUFFERDESC dsbDesc;
HRESULT hr;
LPVOID pLockedBuffer;
DWORD dwLockedSize;

ASSERT(pDS);
ASSERT(lpwfxFormat);
// Create a secondary buffer
ZeroMemory(&dsbDesc, sizeof(dsbDesc));
dsbDesc.dwSize = sizeof(dsbDesc);
m_dwBufferSize = dsbDesc.dwBufferBytes = 
lpwfxFormat -> nBlockAlign * lpwfxFormat -> nSamplesPerSec * SOUND_BUFFER_SIZE;
dsbDesc.dwFlags = DSBCAPS_GLOBALFOCUS;
dsbDesc.lpwfxFormat = lpwfxFormat;
if ( FAILED(hr = pDS -> CreateSoundBuffer(&dsbDesc, &pDSBuffer, NULL)) ||
 FAILED(hr = pDSBuffer -> QueryInterface(IID_IDirectSoundBuffer8, (void**)&pDSBuffer8)) )
return hr;
RELEASENULL(pDSBuffer);

// Fill buffer with silence
m_btSilence = lpwfxFormat -> wBitsPerSample == 8 ? 128 : 0;
if ( FAILED(hr = pDSBuffer8 -> Lock(0, 0, &pLockedBuffer, &dwLockedSize, NULL, 0, DSBLOCK_ENTIREBUFFER)) )
return hr;
::FillMemory(pLockedBuffer, dwLockedSize, m_btSilence);
pDSBuffer8 -> Unlock(pLockedBuffer, dwLockedSize, NULL, 0);

m_pDSBuffer = pDSBuffer8;

return S_OK;
}

LONG CSound::ReceiveData(LPVOID pData, DWORD dwDataSize)
{
HRESULT hr;
DWORD dwPlay;
LPVOID lpPart1, lpPart2;
DWORD dwPart1Size, dwPart2Size, dwMaxSize, dwReceived = 0;
DWORD dwStatus;

ASSERT(m_pDSBuffer);
// Will not receive data if not playing
if ( FAILED(m_pDSBuffer -> GetStatus(&dwStatus)) )
return -1;
if ( !(dwStatus & DSBSTATUS_PLAYING) )
return 0;

// Get current buffer cursors
if ( FAILED(hr = m_pDSBuffer -> GetCurrentPosition(&dwPlay, NULL)) )
return -1;

// Calculate max lock size
if ( dwPlay > m_dwWritePos )
dwMaxSize = dwPlay - m_dwWritePos;
else
dwMaxSize = m_dwBufferSize - m_dwWritePos + dwPlay;

if ( dwDataSize )
{
// Adjust data size
if ( dwDataSize > dwMaxSize)
dwDataSize = dwMaxSize;

// Lock buffer and fill data
if ( FAILED(hr = m_pDSBuffer -> Lock(m_dwWritePos, dwDataSize, &lpPart1, &dwPart1Size, &lpPart2, &dwPart2Size, 0)) )
return -1;
::memcpy(lpPart1, pData, dwPart1Size);
if ( dwPart2Size )
::memcpy(lpPart2, (void**)&((LPBYTE)pData)[dwPart1Size], dwPart2Size);
m_pDSBuffer -> Unlock(lpPart1, dwPart1Size, lpPart2, dwPart2Size);

// Advance our write pointer
dwReceived = dwPart1Size + dwPart2Size;
m_dwWritePos = ( m_dwWritePos + dwReceived ) % m_dwBufferSize;
}

// Fill other parts of the buffer with silence data
if ( dwReceived < dwMaxSize )
{
if ( FAILED(hr = m_pDSBuffer -> Lock(m_dwWritePos, dwMaxSize - dwReceived, &lpPart1, &dwPart1Size, &lpPart2, &dwPart2Size, 0)) )
return dwReceived;
::memset(lpPart1, m_btSilence, dwPart1Size);
if ( dwPart2Size )
::memset(lpPart2, m_btSilence, dwPart2Size);
m_pDSBuffer -> Unlock(lpPart1, dwPart1Size, lpPart2, dwPart2Size);

// Advance our write pointer if full silence written
if ( dwReceived == 0 )
m_dwWritePos = ( m_dwWritePos + dwMaxSize ) % m_dwBufferSize;
}

return dwReceived;
}

HRESULT CSound::Start()
{
ASSERT(m_pDSBuffer);
return m_pDSBuffer -> Play(0, 0, DSBPLAY_LOOPING);
}

HRESULT CSound::Stop()
{
ASSERT(m_pDSBuffer);
return m_pDSBuffer -> Stop();
}

BOOL CSound::IsPlaying()
{
DWORD dwStatus = 0;

ASSERT(m_pDSBuffer);
m_pDSBuffer -> GetStatus(&dwStatus);
return dwStatus & DSBSTATUS_PLAYING;
}

#44


这个是CSoundManager的头文件:

#if !defined SOUND_MANAGER_H__
#define SOUND_MANAGER_H__

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

typedef LPVOID HCHANNEL;
class CSound;

class CSoundManager
{
typedef struct tagSOUNDCHANNEL
{
CSound *pSound;
struct tagSOUNDCHANNEL *pPrev, *pNext;
} SOUNDCHANNEL, *LPSOUNDCHANNEL;
// Protected data members
protected:
LPDIRECTSOUND8 m_pDS;
SOUNDCHANNEL m_scHead, m_scTail;
static CSoundManager *m_pInstance;

// Public interface
public:
CSoundManager();
virtual ~CSoundManager();
HRESULT Initialize(HWND hwndMain);
HRESULT CreateChannel(LPWAVEFORMATEX lpwfxFormat, HCHANNEL *phChannel);
void DeleteChannel(HCHANNEL hChannel);
LONG ReceiveData(HCHANNEL hChannel, LPVOID pData, DWORD dwDataSize);
HRESULT StartChannel(HCHANNEL hChannel);
HRESULT StopChannel(HCHANNEL hChannel);
BOOL IsPlaying(HCHANNEL hChannel);
};

#endif

#45


真见鬼了!居然不然连续发三次!还好有办法,下面继续!

#46


这是CSoundManager的源文件:

#include "stdafx.h"
#include "Sound.h"
#include "SoundManager.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#if !defined RELEASENULL
#define RELEASENULL(p) { if(p) { (p) -> Release(); (p) = NULL; } }
#endif

CSoundManager *CSoundManager::m_pInstance = NULL;

CSoundManager::CSoundManager()
{
m_pDS = NULL;
::ZeroMemory(&m_scHead, sizeof(SOUNDCHANNEL));
::ZeroMemory(&m_scTail, sizeof(SOUNDCHANNEL));
m_scHead.pNext = &m_scTail;
m_scTail.pPrev = &m_scHead;
}

CSoundManager::~CSoundManager()
{
LPSOUNDCHANNEL pChannel = m_scHead.pNext, pCurrent;

while(pChannel != &m_scTail)
{
pCurrent = pChannel;
pChannel = pChannel -> pNext;
delete pCurrent -> pSound;
delete pCurrent;
}
RELEASENULL(m_pDS);
}

HRESULT CSoundManager::Initialize(HWND hwndMain)
{
HRESULT hr;

ASSERT(hwndMain);
ASSERT(m_pInstance == NULL); // Only one instance of CSoundManager an be created
// Create DirectSound object
if ( FAILED(hr = DirectSoundCreate8(NULL, &m_pDS, NULL)) ||
 FAILED(hr = m_pDS -> SetCooperativeLevel(hwndMain, DSSCL_PRIORITY)) )
return hr;

// Remember current instance
m_pInstance = this;

return S_OK;
}

HRESULT CSoundManager::CreateChannel(LPWAVEFORMATEX lpwfxFormat, HCHANNEL *phChannel)
{
HRESULT hr;
CSound *pSound;
LPSOUNDCHANNEL pChannel;

ASSERT(lpwfxFormat);
ASSERT(m_pDS);
// Create CSound object
if ( ( pSound = new CSound ) == NULL )
return E_OUTOFMEMORY;

// Create sound buffer
if ( FAILED(hr = pSound -> Create(m_pDS, lpwfxFormat)) )
{
delete pSound;
return hr;
}

// Add a node to the object list
if ( ( pChannel = new SOUNDCHANNEL ) == NULL )
{
delete pSound;
return E_OUTOFMEMORY;
}
pChannel -> pSound = pSound;
pChannel -> pPrev = m_scTail.pPrev;
pChannel -> pNext = &m_scTail;
m_scTail.pPrev -> pNext = pChannel;
m_scTail.pPrev = pChannel;

*phChannel = pChannel;

return S_OK;
}

void CSoundManager::DeleteChannel(HCHANNEL hChannel)
{
LPSOUNDCHANNEL pChannel = (LPSOUNDCHANNEL)hChannel;

ASSERT(pChannel);
pChannel -> pNext -> pPrev = pChannel -> pPrev;
pChannel -> pPrev -> pNext = pChannel -> pNext;
delete pChannel -> pSound;
delete pChannel;
}

LONG CSoundManager::ReceiveData(HCHANNEL hChannel, LPVOID pData, DWORD dwDataSize)
{
ASSERT(hChannel);
LPSOUNDCHANNEL pChannel = (LPSOUNDCHANNEL)hChannel;

return pChannel -> pSound -> ReceiveData(pData, dwDataSize);
}

HRESULT CSoundManager::StartChannel(HCHANNEL hChannel)
{
ASSERT(hChannel);
LPSOUNDCHANNEL pChannel = (LPSOUNDCHANNEL)hChannel;

return pChannel -> pSound -> Start();
}

HRESULT CSoundManager::StopChannel(HCHANNEL hChannel)
{
ASSERT(hChannel);
LPSOUNDCHANNEL pChannel = (LPSOUNDCHANNEL)hChannel;

return pChannel -> pSound -> Stop();
}

BOOL CSoundManager::IsPlaying(HCHANNEL hChannel)
{
ASSERT(hChannel);
LPSOUNDCHANNEL pChannel = (LPSOUNDCHANNEL)hChannel;

return pChannel -> pSound -> IsPlaying();
}

#47


使用方法很简单:

1、建立一个CSoundManager()对象;
2、与发送端协商一个声音格式,然后调用CreateChannel()建立一个通道;
3、接收开始时调用StartChannel();
4、调用ReceiveData()把送入数据,ReceiveData()返回的长度是实际接收的数据字节数;

要注意的是,最好能够保证定时写入足够的数据。如果进行网络传输,这一点得不到保证的话,那也没关系,只要能够保证定时写入0字节数据就可以了,CSound::ReceiveData()会尽最大可能让缓冲区保持安静。

哦,对了,由于时间很紧,我写得不太规范,像最基本的BufferLost的情况也没有检测,这里仅仅为你提供一个参考。

源程序叫MCDemo,我建立了一个CDataSource类模拟数据源,然后从一个wav文件读取数据,每半秒钟发送一次数据到一个channel,应该就是你要的那种情况了吧?

就这么多了。希望对你有帮助。

#48


太谢谢finalvictory(维克),我试试

#49


CreateChannel(LPWAVEFORMATEX lpwfxFormat, HCHANNEL *phChannel)第二个参数是什么意思呀?

#50


我看了你的程序,基本上正确,有几个地方不对怪我没说清楚。

CreateChannel()的第二个参数是一个返回参数,指向一个内部数据类型SOUNDCHANNEL,只有CSoundManager()可以使用这个类型,这样你就省心多了,每一个通道都有这么一个handle,就像打开文件返回一个handle一样。只要记住这个hChannel就拥有了对这个channel的一切控制权。和所有的handle一样,hChannel不需要你自己分配空间,CreateChannel()会填充它。释放的时候只要调用DeleteChannel()就可以了。事实上,不显式释放hChannel也没有关系,你也看到了,在CSoundManager内部会采用双向链表管理所有已经创建的CSound对象,在它的析构函数中已经有销毁所有对象的行为了。

你会发现CSound所有的函数除了析构函数以外全都是保护的,并且CSoundManager()已经被说明成为CSound的友元,也就是说CSound对象只能从CSoundManager()内部创建。事实上,从纯用户程序员的角度来看,CSound是透明的,完全不用了解它的内部结构和工作原理,只要用hChannel和CSoundManager()打交道就可以了。

另外,StartBuffer()只要调用一次就可以了,我认为最好是在创建完成一个Channel以后马上就StartBuffer()。

还有,关于你的程序结构我提一点建议:(由于我这些代码写得很不规范,要正常工作的话有许多离奇古怪的要求,你别见怪啊,呵呵!)你最好能够用一个定时调用的函数来定时调用ReceiveData(),而接收到的数据放到一个大的数据池中。定时函数如果发现数据池为空,也要调用ReceiveData()向一个channel发送0字节,这可以看成是一个特殊调用,一旦发送了0个字节的数据,CSound就会用静音字节填充声音缓冲区,这样才不会把以前的声音片断播放出来。这个问题我也没有很好的解决,只能用这种笨办法了。你有什么解决办法的话,记得要告诉我哦!

如果你想CSound和CSoundManager在例子程序MCDemo中是怎样使用的,留下你的email,我寄给你。

#1


高个p.用dsound

#2


混音的实现代码以前贴子有的,你搜一下

#3


dsound编程有点头疼,请高手指点。

混音的实现原理是什么,偶一点都不知道,请高手也指点。

#4


如果连 dsound都没信心去学会的话,就不要混了,回家吧

#5


因为我输出单路时用dsound没有用waveOut的效果好,所以我没有用dsound,而且用dsound有个毛病:别的窗口将该窗口覆盖时都放不出声音了,非要让播放窗口激活,而且机器不锁定以后甚至屏保后也放不出声音来。

#6


wyly() ,你说话能不能尊重别人一点,至少我还是一个重点大学的高才硕士生!

#7


啊?~~

#8


呵呵,同意。再高的人也是从菜鸟过来的……

我用DX8做过音频,背景音乐是MIDI,但是其实是用DLS合成后送到主缓冲的。切换出来以后音乐也没有停止。估计要是放WAV也不会有什么问题吧?当然我的协作级别设的很高。你也设的高一点试试,或许不会停止。

#9


呵呵,开玩笑。心情不好

#10


谢谢finalvictory(维克) !

应该在哪个地方设置级别?是在SetCooperativeLevel()中吗?我在其中都设置过了,各种都试过,还是没用呀!而且,怎么用dsound对声音进行混音呀?

#11


混音过程在内部完成,用不着你操心的,你只要调用PlaySegment或者PlaySegmentEx就可以播放了,所有的声音会按照你制定的音量大小和平衡配比正确地播放出来的。

当然了,这是DX8的做法,如果你没有什么底层操作的话,就用DX8吧。因为它比前几个版本的DSound还有DMusic要简单的多了,起码不用直接访问声音缓冲了。

DX8中的IDirectMusicPerformance初始化可以在InitAudio()中一步完成,当然也可以让它返回DSound和DMusic的接口,如果想精细操作的话。我就是一步完成的,属于很经典的那一种模式,你可以看看DirectMusic的例子,都可以失去焦点以后继续播放的,但是DirectSound的例子就不行了。

DX8的音频部分学起来很简单的,我只一个晚上就能应付一般的音频播放情况了,你也没问题的。

#12


finalvictory(维克),可是我是要进行底层操作呀。
我要接收从网络上发过来的音频数据,然后解码播放呀,那应该怎么做呀?

#13


如果你能够解码的话,只要把每个数据流格式化成为一个标准的DSound缓冲,然后播放就可以了,剩下的事情交给DSound。

#14


我给你留言了,去看看吧。

#15


我看到过一段源代码,是在dll中实现同时播放wav的函数,最多可以拨四路,每一路都可以单独控制播放和停止。

#16


我用dsound播放从网络中传来的音频数据时,播放完了好象还有回音,哪位高手能告诉我这是怎么回事吗?

#17


我用dsound播放从网络中传来的数据好象播放完后有回音,哪位高手能告诉我这是怎么回事吗?

#18


#19


#20


#21


高手,用dsound怎样实现混音?
假设我将各路声音都送到一个二级缓冲中去,再播放可以自动实现混音吗?

#22


可以的。要不然还叫DSound吗?

呵呵,又回来了。

#23


谢谢finalvictory!
可是我还是不明白怎样把多路接收过来的声音放到二级缓冲,假设接收的声音解码后的数据为p1,p2,p3...,pn,我是不是要有n个二级缓冲pBuf1,pBuf2,pBuf3,...,pBufn?把数据分别装进去后,怎样跟主缓冲联系起来?

#24


我来说说混音的底层实现原理吧
1  声音是一种波形流,而在计算机中却需要用数字表示。所以在这一过程中
   有一个量化的概念
2  量化过程涉及到几个概念。取样频率、速率及位数
3  那混音是怎样实现的呢?
    声音是一种波,它遵循波的叠加原理。多个声音合成在一起也即就是波
    的叠加后的结果。所以我们混音的步骤就是:
    (1)  首先须把所有待合成的声音统一成一种格式
    (2)  把数据相加即可,超进最大值时,置为最大值。
    数据值是对应波形中的音量,数据相加即音量相加,也即为波的叠加
4  WaveOutXXX(...)这类函数很容易出错,所以还是建议使用dsound

#25


你的“声音解码后的数据”是什么意思?p1,p2,p3,...,pn是指每一个数据采样还是每一路数据采样的头指针?

我想你的意思是后者,那么你要做的就是建立n个流式缓冲区。在初始化阶段,用DSBPLAY_LOOPING作为参数调用每个缓冲区的Play(),这样就开始了监听过程。当接收到了数据并且解码了以后,把声音缓冲Lock()住,然后用你最快的速度把解码后的数据填入Lock()返回的写指针,然后调用Unlock()。当缓冲区内部的读指针指向你的这块数据的时候,你的这段声音自然就会被播放出来了。具体混音过程由DSound实现了。

#26


to freelybird(阿愚):

声音相加可以吗?好像没有这么简单吧?我在Dos下做过支持混音播放的声卡驱动程序,效果很不理想。其原理是在两次中断之间把各路声音混合到后备缓冲区中。我发现如果用简单相加的话,很容易就达到极值了,声音会是一片滋滋啦啦。好像必须把每一路声音都减小一些才行吧,因为我看过allegro的文档,上面说初始化的时候声音通道数不要设得太大,否则声音减小得很厉害,所以就有了上面的想法。

#27


我也觉得waveOut不稳定,所以想用dsound。
可是Lock的时候同时Lock吗?我写了个用dsound进行声音输出的类CDirectSound,里面有个函数是PlayAudio(unsigned char *pBuf,int len)参数当然是解码后的声音数据何声音数据长度,这个类可以实现一路声音的连续播放。我要实现混音,我应该创建几个CDirectSound对象?怎么调用PlayAudio?

#28


你的封装级别太高了!你是不是把整个声音播放过程封装起来了?这样不行的!你应该只封装一个IDirectSoundBuffer*对象。而且要保证你封装的所有对象都是从一个IDirectSound*对象创建的。建议你顺着DSound的结构来,这一点从微软给出的例子程序中也可以看出来。也就是说最好有一个CSoundManager之类的东东负责创建、维护和管理你所有的CDirectSound对象。一个CDirectSound对象对应着一个IDirectSoundBuffer*对象,也即对应着一路声音。

Lock()在相同的IDirectSoundBuffer*对象上面不能嵌套调用,可是在不同的此类对象上面随便用,因为它们管理者不同的声音缓冲区,相互之间不会冲突。所以说如果你把整个声音播放过程封装起来是不能实现多路混音播放的。假设你按照上面说的方法封装好了所有的东西的话,整个过程就要简单的多了:

1、建立一个CDirectSoundManager对象然后初始化DirectSound;
2、根据要接收的声音的路数创建相应多个CDirectSound对象;
3、开辟另外一个线程用轮询的方式来播放流式缓冲区,比如:

EnterCriticalSection(&g_csLocalSoundBuffer);
for ( i = 0 ; i < N ; i++ )
{
  if ( dwSoundLengths[i] )
    pDSound[i] -> PlayAudio(ppSounds[i], dwSoundLengths[i]);
}
LeaveCriticalSection(&g_csLocalSoundBuffer);

4、在主线程中接收网络上来的消息,然后填充ppSounds和dwSoundLengths。

#29


收藏了

#30


if((hr = DirectSoundCreate(NULL,&m_pDirectObject,NULL))==DS_OK)
{
//设定应用程序的声音设备优先级别方式,一般为DSSCL_NORMAL,DSSCL_PRIORITY
 hr = m_pDirectObject->SetCooperativeLevel(AfxGetMainWnd()->m_hWnd,//GetDesktopWindow(),//
DSSCL_PRIORITY );  
 CreateBuffer();
 return true;
}
    


int CDirectSound::PlayAudio(unsigned char *p, int Len)
{
if(Len<=0)
return -1;

if ( LoadData(m_pDSoundBuffer[iPlay],plen) )///Lock数据
m_pDSoundBuffer[iPlay]->Play (0, 0,0) ;

iPlay = (iPlay+1) % MAX_BUF_NUM;

return iPlay;
}

请问我哪些地方可以公用哪些地方要分别创建?

#31


finalvictory(维克) ,那我的封装只需要把除IDirectSound*对象以外的地方封装到都行了,是吗?

#32


我看了你的程序,iPlay是什么,是一个全局变量吗?这样的话你是不是想用一个CSoundBuffer对象搞定所有路的声音?每调用一次PlayAudio(),iPlay就加一以便可以下一次向下一个缓冲区中填写数据?这是你的意思吗?这样是不行的!我分析如下:

声音当然是按照被采集的顺序发送,可是由于网络原因,到达的顺序是不确定的!像你这样处理的话,极有可能把张三的声音送到李四的缓冲区里面去的!这不就乱套了?

另外,你好象还是没有明白我的意思。我是说用一个CSoundBuffer控制一路声音,而不是所有的声音。这样程序结构会清楚许多,自然就好编得多了。

#33


这几天有事情没能来。

 finalvictory(维克),我的iPlay不是你所说的那样,因为一路声音要一个缓冲区播放的话声音是不连续的,所以我采用几个缓冲区的方法来播放使声音连贯。

#34


看不懂,dos下怎么搞呢?

#35


我不管dos下怎么搞。

#36


你的程序写完了吗?我这两天没事儿的时候也在琢磨你这个问题,过些时候我把我的代码贴出来给你看看。

#37


finalvictory(维克) ,太谢谢你了。
如果您有兴趣的话,我可以把我的代码发给您,看我那些地方有问题。

#38


如果2路音频,那就用waveOutOpen打开2个音频设备,然后同时调用waveOutWirte写入音频数据,这样播放时声音是同时播放的。

以上想法我已做过测试,在WINDOWS 2000/XP上均有效,98上不知道行不行,与混音算法相比,这可能是最容易的方法了。

#39


zwok() ,可是哪有两个音频设备呀!一般一台机器只有一个音频设备呀。

#40


你有没有试过同一个音频设备调用2次waveOutOpen呢?

我在win2k/XP上均测试成功

#41


在同一个音频设备上调用2次waveOutOpen,然后会得到2个handle,然后同时向2个HANDLE中写数据,这时是同时播放的,经测试效果不错。

在win2k/xp上均测试成功

#42


嗯,我搞定了,用DX8。有两个类:

CSound:负责维护IDirectSoundBuffer8对象。
CSoundManager:负责维护CSound对象。

下面把核心类的代码给你粘出来:

这是CSound的头文件sound.h:

#if !defined SOUND_H__
#define SOUND_H__

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

class CSound
{
friend class CSoundManager;
// Protected data members
protected:
LPDIRECTSOUNDBUFFER8 m_pDSBuffer;
WAVEFORMATEX m_wfx;
DWORD m_dwBufferSize;
DWORD m_dwWritePos;
BYTE m_btSilence;

// Protected constructor and destructor
protected:
CSound();
virtual ~CSound();

// Internal functions
protected:
HRESULT Create(LPDIRECTSOUND8 pDS, LPWAVEFORMATEX lpwfxFormat);
LONG ReceiveData(LPVOID pData, DWORD dwDataSize);
HRESULT Start();
HRESULT Stop();
BOOL IsPlaying();
};

#endif

#43


这是CSound的原文件sound.cpp:

#include "stdafx.h"
#include "Sound.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#if !defined RELEASENULL
#define RELEASENULL(p) { if(p) { (p) -> Release(); (p) = NULL; } }
#endif

#define SOUND_BUFFER_SIZE 1 // in seconds

CSound::CSound()
{
m_pDSBuffer = NULL;
m_dwWritePos = 0;
}

CSound::~CSound()
{
RELEASENULL(m_pDSBuffer);
}

HRESULT CSound::Create(LPDIRECTSOUND8 pDS, LPWAVEFORMATEX lpwfxFormat)
{
LPDIRECTSOUNDBUFFER pDSBuffer;
LPDIRECTSOUNDBUFFER8 pDSBuffer8;
DSBUFFERDESC dsbDesc;
HRESULT hr;
LPVOID pLockedBuffer;
DWORD dwLockedSize;

ASSERT(pDS);
ASSERT(lpwfxFormat);
// Create a secondary buffer
ZeroMemory(&dsbDesc, sizeof(dsbDesc));
dsbDesc.dwSize = sizeof(dsbDesc);
m_dwBufferSize = dsbDesc.dwBufferBytes = 
lpwfxFormat -> nBlockAlign * lpwfxFormat -> nSamplesPerSec * SOUND_BUFFER_SIZE;
dsbDesc.dwFlags = DSBCAPS_GLOBALFOCUS;
dsbDesc.lpwfxFormat = lpwfxFormat;
if ( FAILED(hr = pDS -> CreateSoundBuffer(&dsbDesc, &pDSBuffer, NULL)) ||
 FAILED(hr = pDSBuffer -> QueryInterface(IID_IDirectSoundBuffer8, (void**)&pDSBuffer8)) )
return hr;
RELEASENULL(pDSBuffer);

// Fill buffer with silence
m_btSilence = lpwfxFormat -> wBitsPerSample == 8 ? 128 : 0;
if ( FAILED(hr = pDSBuffer8 -> Lock(0, 0, &pLockedBuffer, &dwLockedSize, NULL, 0, DSBLOCK_ENTIREBUFFER)) )
return hr;
::FillMemory(pLockedBuffer, dwLockedSize, m_btSilence);
pDSBuffer8 -> Unlock(pLockedBuffer, dwLockedSize, NULL, 0);

m_pDSBuffer = pDSBuffer8;

return S_OK;
}

LONG CSound::ReceiveData(LPVOID pData, DWORD dwDataSize)
{
HRESULT hr;
DWORD dwPlay;
LPVOID lpPart1, lpPart2;
DWORD dwPart1Size, dwPart2Size, dwMaxSize, dwReceived = 0;
DWORD dwStatus;

ASSERT(m_pDSBuffer);
// Will not receive data if not playing
if ( FAILED(m_pDSBuffer -> GetStatus(&dwStatus)) )
return -1;
if ( !(dwStatus & DSBSTATUS_PLAYING) )
return 0;

// Get current buffer cursors
if ( FAILED(hr = m_pDSBuffer -> GetCurrentPosition(&dwPlay, NULL)) )
return -1;

// Calculate max lock size
if ( dwPlay > m_dwWritePos )
dwMaxSize = dwPlay - m_dwWritePos;
else
dwMaxSize = m_dwBufferSize - m_dwWritePos + dwPlay;

if ( dwDataSize )
{
// Adjust data size
if ( dwDataSize > dwMaxSize)
dwDataSize = dwMaxSize;

// Lock buffer and fill data
if ( FAILED(hr = m_pDSBuffer -> Lock(m_dwWritePos, dwDataSize, &lpPart1, &dwPart1Size, &lpPart2, &dwPart2Size, 0)) )
return -1;
::memcpy(lpPart1, pData, dwPart1Size);
if ( dwPart2Size )
::memcpy(lpPart2, (void**)&((LPBYTE)pData)[dwPart1Size], dwPart2Size);
m_pDSBuffer -> Unlock(lpPart1, dwPart1Size, lpPart2, dwPart2Size);

// Advance our write pointer
dwReceived = dwPart1Size + dwPart2Size;
m_dwWritePos = ( m_dwWritePos + dwReceived ) % m_dwBufferSize;
}

// Fill other parts of the buffer with silence data
if ( dwReceived < dwMaxSize )
{
if ( FAILED(hr = m_pDSBuffer -> Lock(m_dwWritePos, dwMaxSize - dwReceived, &lpPart1, &dwPart1Size, &lpPart2, &dwPart2Size, 0)) )
return dwReceived;
::memset(lpPart1, m_btSilence, dwPart1Size);
if ( dwPart2Size )
::memset(lpPart2, m_btSilence, dwPart2Size);
m_pDSBuffer -> Unlock(lpPart1, dwPart1Size, lpPart2, dwPart2Size);

// Advance our write pointer if full silence written
if ( dwReceived == 0 )
m_dwWritePos = ( m_dwWritePos + dwMaxSize ) % m_dwBufferSize;
}

return dwReceived;
}

HRESULT CSound::Start()
{
ASSERT(m_pDSBuffer);
return m_pDSBuffer -> Play(0, 0, DSBPLAY_LOOPING);
}

HRESULT CSound::Stop()
{
ASSERT(m_pDSBuffer);
return m_pDSBuffer -> Stop();
}

BOOL CSound::IsPlaying()
{
DWORD dwStatus = 0;

ASSERT(m_pDSBuffer);
m_pDSBuffer -> GetStatus(&dwStatus);
return dwStatus & DSBSTATUS_PLAYING;
}

#44


这个是CSoundManager的头文件:

#if !defined SOUND_MANAGER_H__
#define SOUND_MANAGER_H__

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

typedef LPVOID HCHANNEL;
class CSound;

class CSoundManager
{
typedef struct tagSOUNDCHANNEL
{
CSound *pSound;
struct tagSOUNDCHANNEL *pPrev, *pNext;
} SOUNDCHANNEL, *LPSOUNDCHANNEL;
// Protected data members
protected:
LPDIRECTSOUND8 m_pDS;
SOUNDCHANNEL m_scHead, m_scTail;
static CSoundManager *m_pInstance;

// Public interface
public:
CSoundManager();
virtual ~CSoundManager();
HRESULT Initialize(HWND hwndMain);
HRESULT CreateChannel(LPWAVEFORMATEX lpwfxFormat, HCHANNEL *phChannel);
void DeleteChannel(HCHANNEL hChannel);
LONG ReceiveData(HCHANNEL hChannel, LPVOID pData, DWORD dwDataSize);
HRESULT StartChannel(HCHANNEL hChannel);
HRESULT StopChannel(HCHANNEL hChannel);
BOOL IsPlaying(HCHANNEL hChannel);
};

#endif

#45


真见鬼了!居然不然连续发三次!还好有办法,下面继续!

#46


这是CSoundManager的源文件:

#include "stdafx.h"
#include "Sound.h"
#include "SoundManager.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#if !defined RELEASENULL
#define RELEASENULL(p) { if(p) { (p) -> Release(); (p) = NULL; } }
#endif

CSoundManager *CSoundManager::m_pInstance = NULL;

CSoundManager::CSoundManager()
{
m_pDS = NULL;
::ZeroMemory(&m_scHead, sizeof(SOUNDCHANNEL));
::ZeroMemory(&m_scTail, sizeof(SOUNDCHANNEL));
m_scHead.pNext = &m_scTail;
m_scTail.pPrev = &m_scHead;
}

CSoundManager::~CSoundManager()
{
LPSOUNDCHANNEL pChannel = m_scHead.pNext, pCurrent;

while(pChannel != &m_scTail)
{
pCurrent = pChannel;
pChannel = pChannel -> pNext;
delete pCurrent -> pSound;
delete pCurrent;
}
RELEASENULL(m_pDS);
}

HRESULT CSoundManager::Initialize(HWND hwndMain)
{
HRESULT hr;

ASSERT(hwndMain);
ASSERT(m_pInstance == NULL); // Only one instance of CSoundManager an be created
// Create DirectSound object
if ( FAILED(hr = DirectSoundCreate8(NULL, &m_pDS, NULL)) ||
 FAILED(hr = m_pDS -> SetCooperativeLevel(hwndMain, DSSCL_PRIORITY)) )
return hr;

// Remember current instance
m_pInstance = this;

return S_OK;
}

HRESULT CSoundManager::CreateChannel(LPWAVEFORMATEX lpwfxFormat, HCHANNEL *phChannel)
{
HRESULT hr;
CSound *pSound;
LPSOUNDCHANNEL pChannel;

ASSERT(lpwfxFormat);
ASSERT(m_pDS);
// Create CSound object
if ( ( pSound = new CSound ) == NULL )
return E_OUTOFMEMORY;

// Create sound buffer
if ( FAILED(hr = pSound -> Create(m_pDS, lpwfxFormat)) )
{
delete pSound;
return hr;
}

// Add a node to the object list
if ( ( pChannel = new SOUNDCHANNEL ) == NULL )
{
delete pSound;
return E_OUTOFMEMORY;
}
pChannel -> pSound = pSound;
pChannel -> pPrev = m_scTail.pPrev;
pChannel -> pNext = &m_scTail;
m_scTail.pPrev -> pNext = pChannel;
m_scTail.pPrev = pChannel;

*phChannel = pChannel;

return S_OK;
}

void CSoundManager::DeleteChannel(HCHANNEL hChannel)
{
LPSOUNDCHANNEL pChannel = (LPSOUNDCHANNEL)hChannel;

ASSERT(pChannel);
pChannel -> pNext -> pPrev = pChannel -> pPrev;
pChannel -> pPrev -> pNext = pChannel -> pNext;
delete pChannel -> pSound;
delete pChannel;
}

LONG CSoundManager::ReceiveData(HCHANNEL hChannel, LPVOID pData, DWORD dwDataSize)
{
ASSERT(hChannel);
LPSOUNDCHANNEL pChannel = (LPSOUNDCHANNEL)hChannel;

return pChannel -> pSound -> ReceiveData(pData, dwDataSize);
}

HRESULT CSoundManager::StartChannel(HCHANNEL hChannel)
{
ASSERT(hChannel);
LPSOUNDCHANNEL pChannel = (LPSOUNDCHANNEL)hChannel;

return pChannel -> pSound -> Start();
}

HRESULT CSoundManager::StopChannel(HCHANNEL hChannel)
{
ASSERT(hChannel);
LPSOUNDCHANNEL pChannel = (LPSOUNDCHANNEL)hChannel;

return pChannel -> pSound -> Stop();
}

BOOL CSoundManager::IsPlaying(HCHANNEL hChannel)
{
ASSERT(hChannel);
LPSOUNDCHANNEL pChannel = (LPSOUNDCHANNEL)hChannel;

return pChannel -> pSound -> IsPlaying();
}

#47


使用方法很简单:

1、建立一个CSoundManager()对象;
2、与发送端协商一个声音格式,然后调用CreateChannel()建立一个通道;
3、接收开始时调用StartChannel();
4、调用ReceiveData()把送入数据,ReceiveData()返回的长度是实际接收的数据字节数;

要注意的是,最好能够保证定时写入足够的数据。如果进行网络传输,这一点得不到保证的话,那也没关系,只要能够保证定时写入0字节数据就可以了,CSound::ReceiveData()会尽最大可能让缓冲区保持安静。

哦,对了,由于时间很紧,我写得不太规范,像最基本的BufferLost的情况也没有检测,这里仅仅为你提供一个参考。

源程序叫MCDemo,我建立了一个CDataSource类模拟数据源,然后从一个wav文件读取数据,每半秒钟发送一次数据到一个channel,应该就是你要的那种情况了吧?

就这么多了。希望对你有帮助。

#48


太谢谢finalvictory(维克),我试试

#49


CreateChannel(LPWAVEFORMATEX lpwfxFormat, HCHANNEL *phChannel)第二个参数是什么意思呀?

#50


我看了你的程序,基本上正确,有几个地方不对怪我没说清楚。

CreateChannel()的第二个参数是一个返回参数,指向一个内部数据类型SOUNDCHANNEL,只有CSoundManager()可以使用这个类型,这样你就省心多了,每一个通道都有这么一个handle,就像打开文件返回一个handle一样。只要记住这个hChannel就拥有了对这个channel的一切控制权。和所有的handle一样,hChannel不需要你自己分配空间,CreateChannel()会填充它。释放的时候只要调用DeleteChannel()就可以了。事实上,不显式释放hChannel也没有关系,你也看到了,在CSoundManager内部会采用双向链表管理所有已经创建的CSound对象,在它的析构函数中已经有销毁所有对象的行为了。

你会发现CSound所有的函数除了析构函数以外全都是保护的,并且CSoundManager()已经被说明成为CSound的友元,也就是说CSound对象只能从CSoundManager()内部创建。事实上,从纯用户程序员的角度来看,CSound是透明的,完全不用了解它的内部结构和工作原理,只要用hChannel和CSoundManager()打交道就可以了。

另外,StartBuffer()只要调用一次就可以了,我认为最好是在创建完成一个Channel以后马上就StartBuffer()。

还有,关于你的程序结构我提一点建议:(由于我这些代码写得很不规范,要正常工作的话有许多离奇古怪的要求,你别见怪啊,呵呵!)你最好能够用一个定时调用的函数来定时调用ReceiveData(),而接收到的数据放到一个大的数据池中。定时函数如果发现数据池为空,也要调用ReceiveData()向一个channel发送0字节,这可以看成是一个特殊调用,一旦发送了0个字节的数据,CSound就会用静音字节填充声音缓冲区,这样才不会把以前的声音片断播放出来。这个问题我也没有很好的解决,只能用这种笨办法了。你有什么解决办法的话,记得要告诉我哦!

如果你想CSound和CSoundManager在例子程序MCDemo中是怎样使用的,留下你的email,我寄给你。