对于Windows的音频采集来讲,方法有不少。能够经过较为上层的多媒体框架去采集,如DirectShow、MediaFoundation这些;也能够经过较为底层的API去采集,如WASAPI(Windows Audio Session API)。这里我介绍的是使用WASAPI的方法,毕竟采集和渲染均可以在这里去实现了。缓存
这里简单介绍一下使用这个API来采集须要了解的两个接口:bash
咱们先来看看Windows下的音频框架关系图:框架
WASAPI有两种采集模式,一种是输入设备的采集,如带麦克风的耳机,麦克风这些;还有一种则是声卡输出的采集,也称做loop back mode。因为两种采集模式是分开的,若是须要同时听到输入设备的声音和声卡输出的声音,则须要对采集到的两路音频流进行混音。oop
首先,咱们须要经过COM接口来获取音频设备枚举实例,代码以下:ui
HRSULT hr;
IMMDeviceEnumerator* pEnumerator = NULL;
hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pEnumerator));
复制代码
枚举音频设备,咱们能够经过EnumAudioEndPoints来一个个进行枚举并选择合适的,但更多状况下咱们是直接使用GetDefaultAudioEndpoint来获取一个默认的,且处于active状态的音频设备。代码以下:spa
IMMDevice* pDevice = NULL;
hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
复制代码
获取完音频设备以后,咱们能够直接调用其activate方法(相似于工厂模式的用法),来获取IAudioClient:code
IAudioClient* pAudioClient = NULL;
hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL,(void**) &pAudioClient);
复制代码
因为咱们使用的是共享模式,所以咱们须要得到Audio Engine的混音格式,经过调用GetMixFormat来得到,代码以下:orm
WAVEFORMATEX* pwfx = NULL;
hr = pAudioClient->GetMixFormat(&pwfx);
复制代码
其中,WAVEFORMATEX的结构体就包含了咱们所须要的混音格式,里面有音频格式,声道数,采样率,样本大小等信息。其中音频格式还须要将这个结构体扩展(也就是强转)成WAVEFORMATEXTENSIBLE这个结构体才能得到,这部分具体可参阅文档。cdn
接下来就是初始化了,即调用Initialize接口。这里面的参数仍是须要特别注意的,咱们先来看看有哪些参数:blog
HRESULT Initialize(
AUDCLNT_SHAREMODE ShareMode,
DWORD StreamFlags,
REFERENCE_TIME hnsBufferDuration,
REFERENCE_TIME hnsPeriodicity,
const WAVEFORMATEX *pFormat,
LPCGUID AudioSessionGuid
);
复制代码
对于第一个参数,以前咱们也说了,咱们是在共享模式下进行采集的,所以这里填AUDCLNT_SHAREMODE_SHARED。
对于第二个参数,若是是初始化输入设备的采集的话,直接填NULL便可。若是是采集声卡输出的,即loop back模式的话,则须要填入AUDCLNT_STREAMFLAGS_LOOPBACK。
第三个是咱们的缓存大小,对于采集到的音乐,WASAPI会帮咱们放到这个缓存中。至于要设置成多大,其实通常是根据你的采集周期来肯定的,你能够根据实际状况简单计算一下,设置成稍微比实际的大一点就好,这样能够避免溢出致使数据丢失的状况。
第四个参数是用于独占模式的,咱们这里用不到,直接填0。
第五个参数就是咱们以前得到的混音格式。
最后一个参数是指定音频Session,这里咱们不关心这个,直接填NULL,让它本身帮咱们建立一个Session,并把咱们的音频流加入到其中。
最后一步即是获取咱们的采集接口,并开始采集,代码以下:
IAudioCaptureClient* pCaptureClient = NULL;
hr = pAudioClient->GetService(
IID_IAudioCaptureClient,
(void**)&pCaptureClient);
hr = pAudioClient->Start();
复制代码
咱们经过GetNextPacketSize来判断下一个数据包的大小,若是不为0的话,咱们就经过GetBuffer来获取一个数据包,依此类推,直到GetNextPacketSize返回的大小为0,说明咱们这一个周期的数据已经获取完了,此时咱们能够休眠一个周期,而后再去获取数据。代码以下:
while(1)
{
//休眠一个周期
Sleep(duration);
hr = pCaptureClient->GetNextPacketSize(&packetLength);
while(packetLength)
{
hr = pCaptureClient->GetBuffer(
&pData,
&numFramesAvailable,
&flags, NULL, NULL);
//DoSomething()....
hr = pCaptureClient->ReleaseBuffer(numFramesAvailable);
hr = pCaptureClient->GetNextPacketSize(&packetLength);
}
}
复制代码
结束采集后调用stop方法便可:
hr = pAudioClient->Stop();
复制代码
若是要同时听到人说话的声音和电脑里的声音,就须要对这两路音频流进行混音,即对两路音频流按必定的比例进行叠加。这一部分能够由第三方的媒体库来完成,如ffmpeg,咱们能够利用amix滤镜来实现。若是要调整音量的话,能够加上volume滤镜来去调节。具体细节不属于本节范畴,所以就不展开细讲了。 须要注意的是,当电脑的中没有产生声音时,咱们是获取不到音频数据的,也就是GetNextPacketSize返回的长度是0.对于混音来讲,所传入的音频数据的长度应该是要对等的,传入采集到的输入设备的一秒钟的音频数据,对应loop back来讲对应的确定也得是一秒钟的音频数据。所以,对于loop back的采集来讲,若此刻只有麦克风的输入,咱们能够适当的填充一些silence字节,来表示这部分是没有声音的,而后再做为输入去混音便可。