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

时间:2021-06-20 03:25:05

     上篇我们将来如何运用Core Audio APIS进行声音数据的采集,下面我们来说说声音数据的播放。

    从第一篇文章我们知道,Core Audio APIS是Window下比较底层的API,使用它来播放声音的时候不像DirectSound这些高层的API那么方便,很多东西要自己来做。

    对于DX9以上的我不太了解,在DX9上,播放音频数据时,你只要设置好相应的数据格式,然后把数据往缓冲区里面塞就OK了;相比之下,Core Audio要复杂一些,下面我们来看看最理想化的一种情况,照例先给出官网地址 https://msdn.microsoft.com/en-us/library/dd316756(v=vs.85).aspx

    hr = CoCreateInstance(

              CLSID_MMDeviceEnumerator, NULL,
              CLSCTX_ALL, IID_IMMDeviceEnumerator,
              (void**)&pEnumerator);
    EXIT_ON_ERROR(hr)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)


    // Tell the audio source which format to use.
    hr = pMySource->SetFormat(pwfx);
    EXIT_ON_ERROR(hr)


    // Get the actual size of the allocated buffer.
    hr = pAudioClient->GetBufferSize(&bufferFrameCount);
    EXIT_ON_ERROR(hr)


   
//注意,这里创建的是IAudioRenderClient,上一篇我们创建的是IAudioCaptureClient
    hr = pAudioClient->GetService(
                         IID_IAudioRenderClient,
                         (void**)&pRenderClient);
    EXIT_ON_ERROR(hr)


    // Grab the entire buffer for the initial fill operation.
    hr = pRenderClient->GetBuffer(bufferFrameCount, &pData);
    EXIT_ON_ERROR(hr)


    // Load the initial data into the shared buffer.
    hr = pMySource->LoadData(bufferFrameCount, pData, &flags);
    EXIT_ON_ERROR(hr)


    hr = pRenderClient->ReleaseBuffer(bufferFrameCount, flags);
    EXIT_ON_ERROR(hr)


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


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


    //上面那些之前解释过,并没有什么太大的区别,着重来看下这个循环的代码
    // Each loop fills about half of the shared buffer.
    while (flags != AUDCLNT_BUFFERFLAGS_SILENT)
    {
        // Sleep for half the buffer duration.
        Sleep((DWORD)(hnsActualDuration/REFTIMES_PER_MILLISEC/2));


        //获取缓冲区内还未播放完的数据帧数
        hr = pAudioClient->GetCurrentPadding(&numFramesPadding);
        EXIT_ON_ERROR(hr)


        //获取空余的缓冲区长度,之前说过,缓冲区有个最大的长度,这个你可以设置的
        numFramesAvailable = bufferFrameCount - numFramesPadding;


        // 写入数据
        hr = pRenderClient->GetBuffer(numFramesAvailable, &pData);
        EXIT_ON_ERROR(hr)


        // Get next 1/2-second of data from the audio source.
        hr = pMySource->LoadData(numFramesAvailable, pData, &flags);
        EXIT_ON_ERROR(hr)


        hr = pRenderClient->ReleaseBuffer(numFramesAvailable, flags);
        EXIT_ON_ERROR(hr)
    }


    // Wait for last data in buffer to play before stopping.
    Sleep((DWORD)(hnsActualDuration/REFTIMES_PER_MILLISEC/2));


    hr = pAudioClient->Stop();  // Stop playing.

   

   上面是最理想也是最简单的情况。何为最理想,就是你想要播放的音频数据,它的数据格式刚好与播放设备所支持的格式是一致的,这样这里就不涉及到数据的重抽样。也许这样说的有点模糊,下面我们来举个例子。

    假如你设备所支持的格式是:采集频率16000HZ;而你的数据的采集频率是8000HZ.(其它参数我们默认是一样的,简单一些)。如果我们什么都不做,直接把数据写入到缓冲区,会有什么效果?

     没错,我们可以猜测到,声音的播放速度快了一倍,为什么?根据数据量的计算,我们输入的数据量少了一倍,本来我们输入2秒的数据量,结果设备只用了一秒就播放完了,这样播放的效果自然是不行的,为了达到理想的效果,我们必须对输入的数据进行一次重抽样,经过重抽样后的数据才符合设备的要求