DirectSound捕获千兆字节的帧而不是兆字节

时间:2018-01-18 00:23:19

标签: c++ audio capture directsound

我正在使用DirectSound捕获桌面的声音,使用MSDN的教程https://msdn.microsoft.com/en-us/library/windows/desktop/ee416968(v=vs.85).aspx

问题在于,当我开始捕捉时,我的WAV文件在几秒钟内达到数百MB甚至GB。当我播放WAV文件时,5秒的音频捕获变为30分钟,并且相同的帧声音重复1000次。我注意到WAIT_OBJECT_0 + 1和2从未被解雇。谢谢。

bool captureSound = false;
wav_header wavFile; // the wav file
UINT totalData = 0;
LPDIRECTSOUNDCAPTURE8 capturer = NULL;
GUID guid = DSDEVID_DefaultCapture;
HRESULT err;
DSCBUFFERDESC dscbd;
LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB8;
DWORD g_dwNextCaptureOffset = 0;
DWORD g_dwCaptureBufferSize = 0;
DWORD g_dwNotifySize = 0;

#define cEvents  3

WAVEFORMATEX         wfx;  
HANDLE     rghEvent[cEvents] = {0,0,0};
DSBPOSITIONNOTIFY  rgdsbpn[cEvents];

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Place code here.
    MSG msg;
    HACCEL hAccelTable;
    BOOL  bDone; 

    // Initialize global strings
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_WIN32PROJECT2, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // Perform application initialization:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32PROJECT2));


    bDone = FALSE;
    // Main message loop:
    while( !bDone ) {
         dwResult = MsgWaitForMultipleObjects( 3, rghEvent,    
                                             FALSE, INFINITE, QS_ALLEVENTS ); 
         switch( dwResult ) {
            case WAIT_OBJECT_0 + 0: 
                if(FAILED(RecordCapturedData())) MessageBox(NULL,TEXT("Error!"), TEXT("Error"), MB_OK);
                break;
            case WAIT_OBJECT_0 + 1: 
                if(FAILED(RecordCapturedData())) MessageBox(NULL,TEXT("Error!"), TEXT("Error"), MB_OK);
                break;
            case WAIT_OBJECT_0 + 2: 
                if(FAILED(RecordCapturedData())) MessageBox(NULL,TEXT("Error!"), TEXT("Error"), MB_OK);
                break;
            case WAIT_OBJECT_0 + 3:
                //MessageBox(NULL,TEXT("wait3"), TEXT("Error"), MB_OK);
                // Windows messages are available
                while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
                    {
                        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
                        {
                            TranslateMessage(&msg);
                            DispatchMessage(&msg);
                        }
                        if( msg.message == WM_QUIT )   
                            bDone = TRUE; 
                    }
                break;
         }
    }

    //CloseHandle(rghEvent);

    return (int) msg.wParam;
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;

    switch (message)
    {
    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        // Parse the menu selections:
        switch (wmId)
        {
        case IDM_CAPTURESOUND:
            captureSound = true;
            if (FAILED(createWav(&wavFile)))
            {
                closeWav(wavFile.pFile);
                MessageBox(NULL,TEXT("Error creating the sound file!"), TEXT("Error"), MB_OK);
            }

            // add the WAV header
            for(int i = 0; i < 4; i++) {
                fputc(wavFile.riff_header[i], wavFile.pFile);
            }
            fwrite((char*)&wavFile.wav_size,sizeof(int),1,wavFile.pFile);
            for(int i = 0; i < 4; i++) {
                fputc(wavFile.wave_header[i], wavFile.pFile);
            }
            for(int i = 0; i < 4; i++) {
                fputc(wavFile.fmt_header[i], wavFile.pFile);
            }
            fwrite((const char*)&wavFile.fmt_chunk_size,4,1,wavFile.pFile);
            fwrite((const char*)&wavFile.audio_format,2,1,wavFile.pFile);
            fwrite((const char*)&wavFile.num_channels,2,1,wavFile.pFile);
            fwrite((const char*)&wavFile.sample_rate,4,1,wavFile.pFile);
            fwrite((const char*)&wavFile.byte_rate,4,1,wavFile.pFile);
            fwrite((const char*)&wavFile.sample_alignment,2,1,wavFile.pFile);
            fwrite((const char*)&wavFile.bit_depth,2,1,wavFile.pFile);
            for(int i = 0; i < 4; i++) {
                fputc(wavFile.data_header[i], wavFile.pFile);
            }
            fwrite((const char*)&wavFile.data_bytes,4,1,wavFile.pFile);

            /* Use DirectSoundCaptureCreate8() to create and initialize an object and get the pointer (pDSC8) to IDirectSoundCapture8 */
            if(FAILED(DirectSoundCaptureCreate8(&guid, &capturer, NULL))) {
                ErrorExit(TEXT("DirectSoundCaptureCreate8"));
            }

            /* Use the method CreateCaptureBuffer() of IDirectSoundCapture8(pDSC8->CreateSoundBuffer()) to create 
            and initialize an object and get the  pointer (pDSCB) to IDirectSoundCaptureBuffer. */
            if(FAILED(CreateCaptureBuffer(capturer,&pDSCB8))) {
                 ErrorExit(TEXT("CreateCaptureBuffer"));    
            }

            /* Use the method QueryInterface of  IDirectSoundCaptureBuffer8(pDSCB8->QueryInterface()) to get a pointer(lpDsNotify) to the interface IDirectSoundNotify8. */
            if(FAILED(SetCaptureNotifications(pDSCB8))) {
                 ErrorExit(TEXT("SetCaptureNotifications"));    
            }

            /* Start capturing */
            if(FAILED(pDSCB8->Start( DSCBSTART_LOOPING ) ) )
                ErrorExit(TEXT("Start"));   
            else MessageBox(NULL,TEXT("Started capturing!"), TEXT("Good"), MB_OK);
            break;
        case IDM_OPRESTE:
            captureSound = false;

            // Stop the buffer, and read any data that was not    
            // caught by a notification 

            if( FAILED(pDSCB8->Stop() ) )   
                 ErrorExit(TEXT("Stop"));

            if(FAILED(RecordCapturedData())) MessageBox(NULL,TEXT("ERROR "), TEXT("Error"), MB_OK);


            /* Update the fields in the WAV header and close the file */
            fseek (wavFile.pFile, 4 , SEEK_SET );
            wavFile.wav_size += totalData;
            fwrite((char*)&wavFile.wav_size,sizeof(int),1,wavFile.pFile);

            wavFile.data_bytes += totalData;
            fseek (wavFile.pFile, 40 , SEEK_SET );
            fwrite((char*)&wavFile.data_bytes,sizeof(int),1,wavFile.pFile);

            closeWav(wavFile.pFile);  
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd); // POSTS THE MESSAGE WM_DESTROY TO DESTROY THE CREATED WINDOW.
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_CREATE: 
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

HRESULT CreateCaptureBuffer(LPDIRECTSOUNDCAPTURE8 pDSC, 
                            LPDIRECTSOUNDCAPTUREBUFFER8* ppDSCB8)
{
  HRESULT hr;
  DSCBUFFERDESC               dscbd;
  LPDIRECTSOUNDCAPTUREBUFFER  pDSCB;

  // Set up WAVEFORMATEX for 44.1 kHz 16-bit stereo. 
  WAVEFORMATEX                wfx =
  {WAVE_FORMAT_PCM, wavFile.num_channels, wavFile.sample_rate, wavFile.byte_rate, wavFile.sample_alignment, wavFile.bit_depth, 0};
    // wFormatTag, nChannels, nSamplesPerSec, mAvgBytesPerSec,
    // nBlockAlign, wBitsPerSample, cbSize

  if ((NULL == pDSC) || (NULL == ppDSCB8)) return E_INVALIDARG;
  dscbd.dwSize = sizeof(DSCBUFFERDESC);
  dscbd.dwFlags = 0;
  dscbd.dwBufferBytes = wfx.nAvgBytesPerSec;
  dscbd.dwReserved = 0;
  dscbd.lpwfxFormat = &wfx;
  dscbd.dwFXCount = 0;
  dscbd.lpDSCFXDesc = NULL;

  g_dwNotifySize = MAX( 1024, wavFile.byte_rate / 8 );
  g_dwNotifySize -= g_dwNotifySize % wavFile.sample_alignment;
  g_dwCaptureBufferSize = g_dwNotifySize * 3;

  if (SUCCEEDED(hr = pDSC->CreateCaptureBuffer(&dscbd, &pDSCB, NULL)))
  {
    /* Use the method QueryInterface of  IDirectSoundCaptureBuffer(pDSCB->QueryInterface()) to get a 
    pointer(pDSCB8) to the interface IDirectSoundCaptureBuffer8 */
    hr = pDSCB->QueryInterface(IID_IDirectSoundCaptureBuffer8, (LPVOID*)ppDSCB8);
    pDSCB->Release();  


  }
  return hr;
}

HRESULT SetCaptureNotifications(LPDIRECTSOUNDCAPTUREBUFFER8 pDSCB)
{
  LPDIRECTSOUNDNOTIFY8 pDSNotify;
  HRESULT    hr;

  if (NULL == pDSCB) return E_INVALIDARG;
  if (FAILED(hr = pDSCB->QueryInterface(IID_IDirectSoundNotify, (LPVOID*)&pDSNotify)))
  {
    return hr;
  }
  if (FAILED(hr = pDSCB->GetFormat(&wfx, sizeof(WAVEFORMATEX), NULL)))
  {
    return hr;
  }

  // Create events.
  for (int i = 0; i < cEvents; ++i)
  {
    rghEvent[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (NULL == rghEvent[i])
    {
      hr = GetLastError();
      return hr;
    }
  }

  // Describe notifications. 

  rgdsbpn[0].dwOffset = (wfx.nAvgBytesPerSec/2) -1;
  rgdsbpn[0].hEventNotify = rghEvent[0];

  rgdsbpn[1].dwOffset = wfx.nAvgBytesPerSec - 1;
  rgdsbpn[1].hEventNotify = rghEvent[1];

  rgdsbpn[2].dwOffset = DSBPN_OFFSETSTOP;
  rgdsbpn[2].hEventNotify = rghEvent[2];

  /* Use the SetNotificationPositions() of IDirectSoundNotify8(lpDsNotify->SetNotificationPositions())
  to set the notification buffer positions */

  hr = pDSNotify->SetNotificationPositions(cEvents, rgdsbpn);
  pDSNotify->Release();
  return hr;
}

HRESULT RecordCapturedData() 
    {
      HRESULT hr;
      VOID* pbCaptureData  = NULL;
      DWORD dwCaptureLength;
      VOID* pbCaptureData2 = NULL;
      DWORD dwCaptureLength2;
      VOID* pbPlayData   = NULL;
      UINT  dwDataWrote;
      DWORD dwReadPos;
      LONG lLockSize;

      if (NULL == pDSCB8)
          MessageBox(NULL,TEXT("Empty buffer!"), TEXT("Error"), MB_OK);
      if (NULL == wavFile.pFile)
          MessageBox(NULL,TEXT("Empty .wav file!"), TEXT("Error"), MB_OK);

      if (FAILED (hr = pDSCB8->GetCurrentPosition( 
        NULL, &dwReadPos)))
          MessageBox(NULL,TEXT("Failed to get current position!"), TEXT("Error"), MB_OK);

      // Lock everything between the private cursor 
      // and the read cursor, allowing for wraparound.

      lLockSize = dwReadPos - g_dwNextCaptureOffset;
      if( lLockSize < 0 ) lLockSize += g_dwCaptureBufferSize;

      // Block align lock size so that we are always write on a boundary   
      //lLockSize -= (lLockSize % g_dwNotifySize);   

      if( lLockSize == 0 ) return S_FALSE;

      if (FAILED(hr = pDSCB8->Lock( 
            g_dwNextCaptureOffset, lLockSize, 
            &pbCaptureData, &dwCaptureLength, 
            &pbCaptureData2, &dwCaptureLength2, 0L)))
        MessageBox(NULL,TEXT("Lock failed!"), TEXT("Error"), MB_OK);

      // Write the data. This is done in two steps
      // to account for wraparound.

      if (FAILED( addData(&wavFile, (BYTE*)pbCaptureData, dwCaptureLength, &dwDataWrote)))
        MessageBox(NULL,TEXT("Error writting to the file!"), TEXT("Error"), MB_OK); 

      if (pbCaptureData2 != NULL)
      {
        if (FAILED( addData(&wavFile, (BYTE*)pbCaptureData2, dwCaptureLength2, &dwDataWrote)))
          MessageBox(NULL,TEXT("Error writting to the file 2!"), TEXT("Error"), MB_OK); 
      }

      // Unlock the capture buffer.

     pDSCB8->Unlock( pbCaptureData, dwCaptureLength, 
        pbCaptureData2, dwCaptureLength2  );

      // Move the capture offset forward.

      g_dwNextCaptureOffset += dwCaptureLength; 
      g_dwNextCaptureOffset %= g_dwCaptureBufferSize; 
      g_dwNextCaptureOffset += dwCaptureLength2; 
      g_dwNextCaptureOffset %= g_dwCaptureBufferSize; 

      totalData += (dwCaptureLength + dwCaptureLength2);
      return S_OK;
    }

Wav writer实用程序

#include "stdafx.h"
#include "Wav.h"

HRESULT createWav(wav_header* pWav) {
    pWav->pFile = fopen("sound.wav","w");
    if(pWav->pFile == NULL) {
        return 0;
    }
    strcpy(pWav->riff_header, "RIFF");
    strcpy(pWav->wave_header, "WAVE");
    strcpy(pWav->fmt_header, "fmt ");
    pWav->fmt_chunk_size = 16;
    pWav->audio_format = 1;
    pWav->num_channels = 2;
    pWav->sample_rate = 44100; // 44.1 KHz - CD-quality audio
    pWav->bit_depth = 16; // Bits per Sample – The number of bits available for one sample. 
    pWav->sample_alignment = pWav->num_channels * (pWav->bit_depth)/8; // This is the number of bytes in a frame
    pWav->byte_rate = pWav->sample_rate * pWav->sample_alignment; // number of bytes per second captured
    strcpy(pWav->data_header, "data");
    pWav->data_bytes = 0; // n * frames , n = 0 empty data default, to be updated each time new data is added
    pWav->wav_size = 36 + pWav->data_bytes; // to be updated each time new data is added

    return S_OK;
}

HRESULT closeWav(FILE * pFile) {
    fclose(pFile);
    return S_OK;
}

HRESULT addData(wav_header* pWav, VOID* data,UINT dataLength, UINT* dataWrote) {
    // set the positon back at the end of the file
    fseek (pWav->pFile, 0 , SEEK_END );
    *dataWrote = fwrite((const char*)data,sizeof(byte),(size_t)dataLength, pWav->pFile);
    return S_OK;
}

0 个答案:

没有答案