桌面录音

Posted xianjian_x

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了桌面录音相关的知识,希望对你有一定的参考价值。

  公司做一个项目,需要把 windows 扬声器的声音捕捉下来,这个可以参考MSDN文档,上面有示例程序:
Loopback Recording

Capturing a Stream

这程序并不完整,还需要做一些修改才行:

下面是我 调试通过的代码:

#include <Mmdeviceapi.h>
#include <Audioclient.h>

//-----------------------------------------------------------
// Record an audio stream from the default audio capture
// device. The RecordAudiostream function allocates a shared
// buffer big enough to hold one second of PCM audio data.
// The function uses this buffer to stream data from the
// capture device. The main loop runs every 1/2 second.
//-----------------------------------------------------------

// REFERENCE_TIME time units per second and per millisecond
#define REFTIMES_PER_SEC  10000000
#define REFTIMES_PER_MILLISEC  10000

#define EXIT_ON_ERROR(hres)  \\
    if (FAILED(hres))  goto Exit; 

#define SAFE_RELEASE(punk)  \\
    if ((punk) != NULL)  \\
                 (punk)->Release(); (punk) = NULL; 


const CLSID CLSID_MMDeviceEnumerator    = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator       = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioClient              = __uuidof(IAudioClient);
const IID IID_IAudioCaptureClient       = __uuidof(IAudioCaptureClient);



class MyAudioSink 
public:
        //WAVEFORMATEX *pwfx = NULL;
        int     SetFormat(WAVEFORMATEX *pwfx);

        int     CopyData(char * pData,UINT32 numFramesAvailable,BOOL *pbDone);
;



int MyAudioSink::SetFormat(WAVEFORMATEX *pwfx)

    printf("wFormatTag is %x\\n",        pwfx->wFormatTag);
    printf("nChannels is %x\\n",         pwfx->nChannels);
    printf("nSamplesPerSec is %d\\n",    pwfx->nSamplesPerSec);
    printf("nAvgBytesPerSec is %d\\n",   pwfx->nAvgBytesPerSec);
    printf("wBitsPerSample is %d\\n",    pwfx->wBitsPerSample);

    return 0;


int MyAudioSink::CopyData(char *    pData,UINT32 numFramesAvailable,BOOL *pbDone)

    if(pData != NULL)
        fwrite(pData, numFramesAvailable ,1,fp);

    return 0;


/// pwfx->nSamplesPerSec    = 44100; 
/// 不支持修改采样率, 看来只能等得到数据之后再 swr 转换了 
BOOL AdjustFormatTo16Bits(WAVEFORMATEX *pwfx)

    BOOL bRet(FALSE);

    if(pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
    
        pwfx->wFormatTag = WAVE_FORMAT_PCM;
        pwfx->wBitsPerSample = 16;
        pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
        pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;

        bRet = TRUE;
    
    else if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
    
        PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(pwfx);
        if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat))
        
           pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
           pEx->Samples.wValidBitsPerSample = 16;
           pwfx->wBitsPerSample = 16;
           pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
           pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;

           bRet = TRUE;
         
    

    return bRet;


HRESULT RecordAudioStream(MyAudioSink *pMySink)

    HRESULT             hr;
    REFERENCE_TIME      hnsActualDuration;
    UINT32              bufferFrameCount;
    UINT32              numFramesAvailable;
    BYTE *              pData;
    DWORD               flags;
    REFERENCE_TIME      hnsDefaultDevicePeriod(0);

    REFERENCE_TIME          hnsRequestedDuration    = REFTIMES_PER_SEC;
    IMMDeviceEnumerator*    pEnumerator             = NULL;
    IMMDevice *             pDevice                 = NULL;
    IAudioClient *          pAudioClient            = NULL;
    IAudioCaptureClient *   pCaptureClient          = NULL;
    WAVEFORMATEX *          pwfx                    = NULL;
    UINT32                  packetLength            = 0;
    BOOL                    bDone                   = FALSE;
    HANDLE                  hTimerWakeUp            = NULL; 

    hr = CoCreateInstance( CLSID_MMDeviceEnumerator, NULL,CLSCTX_ALL, IID_IMMDeviceEnumerator,(void**)&pEnumerator );

    EXIT_ON_ERROR(hr)

    hr = pEnumerator->GetDefaultAudioEndpoint( eRender /*eCapture*/, eConsole, &pDevice );

    EXIT_ON_ERROR(hr)

    hr = pDevice->Activate( IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
    EXIT_ON_ERROR(hr)

    hr = pAudioClient->GetDevicePeriod(&hnsDefaultDevicePeriod, NULL);

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

    AdjustFormatTo16Bits(pwfx);

    hTimerWakeUp = CreateWaitableTimer(NULL, FALSE, NULL);


    hr = pAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,AUDCLNT_STREAMFLAGS_LOOPBACK /*0*/ ,0 ,0,pwfx,NULL );

    EXIT_ON_ERROR(hr)

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

    hr = pAudioClient->GetService( IID_IAudioCaptureClient, (void**)&pCaptureClient);
    EXIT_ON_ERROR(hr)

    LARGE_INTEGER liFirstFire;
    liFirstFire.QuadPart = -hnsDefaultDevicePeriod / 2; // negative means relative time
    LONG lTimeBetweenFires = (LONG)hnsDefaultDevicePeriod / 2 / (10 * 1000); // convert to milliseconds

    BOOL bOK = SetWaitableTimer(hTimerWakeUp,&liFirstFire,lTimeBetweenFires,NULL, NULL, FALSE);

    // 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)
    HANDLE waitArray[1] =  /*htemp hEventStop,*/ hTimerWakeUp ;

    // Each loop fills about half of the shared buffer.
    while (bDone == FALSE)
    
        // Sleep for half the buffer duration.
        //Sleep(hnsActualDuration/REFTIMES_PER_MILLISEC/2);
        //WaitForSingleObject(hTimerWakeUp,INFINITE);
        WaitForMultipleObjects(sizeof(waitArray)/sizeof(waitArray[0]), waitArray, FALSE, INFINITE);
        //WaitForMultipleObjects(sizeof(waitArray)/sizeof(waitArray[0]), waitArray, FALSE, INFINITE);

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

        while (packetLength != 0)
        
            // Get the available data in the shared buffer.
            hr = pCaptureClient->GetBuffer( &pData, &numFramesAvailable,&flags, NULL, NULL);
            EXIT_ON_ERROR(hr)

            //if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
            //
            //  pData = NULL;  // Tell CopyData to write silence.
            //

            // Copy the available capture data to the audio sink.
            hr = pMySink->CopyData((char *) pData, numFramesAvailable * pwfx->nBlockAlign, &bDone);
            EXIT_ON_ERROR(hr)

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

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

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

Exit:
    CoTaskMemFree(pwfx);
    SAFE_RELEASE(pEnumerator)
    SAFE_RELEASE(pDevice)
    SAFE_RELEASE(pAudioClient)
    SAFE_RELEASE(pCaptureClient)

    return hr;



int _tmain(int argc, _TCHAR* argv[])

    fp      = fopen("F://work//record.pcm","wb");
    CoInitialize(NULL);
    MyAudioSink test;

    RecordAudioStream(&test);

    return 0;

留着以后使用

以上是关于桌面录音的主要内容,如果未能解决你的问题,请参考以下文章

微信怎么录音

信创操作系统--麒麟Kylin桌面版 (项目八 多媒体软件:图像查看图像处理音频播放录音机)

微信小程序录音界面以及功能实现

华为手机微信语音转文字怎么设置声音

什么品牌声卡最好?求推荐。

什么是电子数据保管?有什么用?