Windows下Core Audio APIS 音频应用开发(二)

时间:2020-11-30 03:25:33

     对于一个音频应用程序,最基本也是最重要的两个点就是:音频数据的采集;音频数据的播放。下面我们来看下如何用Core Audio APIS进行音频数据的采集。

    最权威的学习资料无疑是微软的MSDN官方资料,先给出链接地址,大家可以先去阅读下文档,虽然是英文,但挺好理解的,https://msdn.microsoft.com/en-us/library/dd370800(v=vs.85).aspx

    首先我们来了解下面几个概念:

    1. IMMDevice : 创建音频设备终端,我们可以把它简单的理解为设备对象

    2. IAudioClient : 创建一个用来管理音频数据流的对象,应用程序通过这个对象可以获取的音频设备里的数据,我们可以把它想象成一个大水池,里面都是一些数据

    3.  IAudioCaptureClient : 很明显,专用于获取采集数据的对象,它还有个兄弟IAudioRenderClient

    下面直接上官网示例代码,解释下代码大家就清楚怎么做了


   //首先枚举你的音频设备,你可以在这个时候获取到你机器上所有可用的设备,并指定你需要用到的那个设置
    hr = CoCreateInstance(
CLSID_MMDeviceEnumerator, NULL,
CLSCTX_ALL, IID_IMMDeviceEnumerator,
(void**)&pEnumerator);
EXIT_ON_ERROR(hr)

hr = pEnumerator->GetDefaultAudioEndpoint(
eCapture, eConsole, &pDevice);
EXIT_ON_ERROR(hr)

   // 创建一个管理对象,通过它可以获取到你需要的一切数据
hr = pDevice->Activate(
IID_IAudioClient, CLSCTX_ALL,
NULL, (void**)&pAudioClient);
EXIT_ON_ERROR(hr)

hr = pAudioClient->GetMixFormat(&pwfx);
EXIT_ON_ERROR(hr)

   //初始化管理对象,在这里,你可以指定它的最大缓冲区长度,这个很重要,应用程序控制数据块的大小以及延时长短都靠这里的初始化,具体参数大家看看文档解释
hr = pAudioClient->Initialize(
AUDCLNT_SHAREMODE_SHARED,
0,
hnsRequestedDuration,
0,
pwfx,
NULL);
EXIT_ON_ERROR(hr)

//这个buffersize,指的是缓冲区最多可以存放多少帧的数据量
hr = pAudioClient->GetBufferSize(&bufferFrameCount);
EXIT_ON_ERROR(hr)

    //创建采集管理接口,这个接口很简单,没什么重要的东西
hr = pAudioClient->GetService(
IID_IAudioCaptureClient,
(void**)&pCaptureClient);
EXIT_ON_ERROR(hr)

// Notify the audio sink which format to use.
hr = pMySink->SetFormat(pwfx);
EXIT_ON_ERROR(hr)

// Calculate the actual duration of the allocated buffer.
hnsActualDuration = (double)REFTIMES_PER_SEC *
bufferFrameCount / pwfx->nSamplesPerSec;

hr = pAudioClient->Start(); // Start recording.
EXIT_ON_ERROR(hr)

// Each loop fills about half of the shared buffer.
while (bDone == FALSE)
{
//让程序暂停运行一段时间,缓冲区里在这段时间会被填充数据
Sleep(hnsActualDuration/REFTIMES_PER_MILLISEC/2);

hr = pCaptureClient->GetNextPacketSize(&packetLength);
EXIT_ON_ERROR(hr)

while (packetLength != 0)
{
//锁定缓冲区,获取数据
hr = pCaptureClient->GetBuffer(
&pData,
&numFramesAvailable,
&flags, NULL, NULL);
EXIT_ON_ERROR(hr)

if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
{
pData = NULL;
}

hr = pMySink->CopyData(
pData, numFramesAvailable, &bDone);
EXIT_ON_ERROR(hr)

hr = pCaptureClient->ReleaseBuffer(numFramesAvailable);
EXIT_ON_ERROR(hr)

hr = pCaptureClient->GetNextPacketSize(&packetLength);
EXIT_ON_ERROR(hr)
}
}

hr = pAudioClient->Stop();
EXIT_ON_ERROR(hr)
     代码就这么多,这是官网给出的最简单的一个关于获取音频数据的示例,这里面取到的数据是最原始的,我们可以对这些数据进去任何处理,达到我们需要的效果。

     对于初学者来说(比如笔者),文中的pMySink会让大家感到迷惑。大家都能猜到这个是自定义的一个类的对象,用来拷贝数据的,但里面做了一些什么,我们却不清楚。我这里多说一些(大牛请无视下面的话):

      音频数据量的计算:数据量(字节/秒)= (采样频率(Hz)*采样位数(bit)*声道数)/ 8

我们回顾下代码,创建完管理对象后,我们就可以获取到当前设备的音频数据的格式,然后对pMySink设置了数据格式

hr = pAudioClient->GetMixFormat(&pwfx);//这里面就可以获取的数据格式:频率,采集位数,声道数 

hr = pMySink->SetFormat(pwfx);//指定数据格式,计算数据量

这就决定了,pMySink每一次读取数据量的大小,保证我们在获取的每一帧数据都是正确的

hr = pMySink->CopyData(pData, numFramesAvailable, &bDone);

这里就是简单的拷贝数据了