Writing to a WAV File

[The feature associated with this page, DirectSound, is a legacy feature. It has been superseded by WASAPI and Audio Graphs. Media Casting have been optimized for Windows 10 and Windows 11. Microsoft strongly recommends that new code use Media Casting instead of DirectSound, when possible. Microsoft suggests that existing code that uses the legacy APIs be rewritten to use the new APIs if possible.]

WAV files are in the Resource Interchange File Format (RIFF), which consists of a variable number of named chunks containing either header information (for example, the format of sound samples) or data (the samples themselves). The Win32 API supplies functions for opening and closing RIFF files, seeking to chunks, and so on. The names of these functions all start with "mmio".

The DirectSound API does not include methods for writing to WAV files. However, the DXUTsound.cpp file implements a CWaveFile class that has the following methods for managing capture files:

  • Open. Opens a file and writes the header chunks.

  • Write. Writes from a buffer to the data chunk and advances the write cursor.

  • Close. Writes the size of the data chunk in the header and closes the file.

The first step in writing a WAV file is to call the CWaveFile::Open method. This creates the file and writes the WAV format chunk. The parameters are the filename, a pointer to an initialized WAVEFORMATEX structure, and the WAVEFILE_WRITE flag. The method returns an HRESULT.

The following code opens a WAV file for writing:

    CWaveFile   g_pWaveFile;
    WAVEFORMATEX  wfxInput;
     
    ZeroMemory( &wfxInput, sizeof(wfxInput));
    wfxInput.wFormatTag = WAVE_FORMAT_PCM;
    wfxInput.nSamplesPerSec = 22050
    wfxInput.wBitsPerSample =  8; 
    wfxInput.nChannels = 1;
    wfxInput.nBlockAlign = 
        wfxInput.nChannels * (wfxInput.wBitsPerSample / 8);
    wfxInput.nAvgBytesPerSec = 
        wfxInput.nBlockAlign * wfxInput.nSamplesPerSec;
     
    g_pWaveFile = new CWaveFile;
    if (FAILED(g_pWaveFile->Open("mywave.wav", &wfxInput,
        WAVEFILE_WRITE)))
    {
      g_pWaveFile->Close();
    }

The application can now begin copying data from the capture buffer to the file.

The following example function is called each time the read cursor reaches a notification position. In this function, the following global variables are used:

  • g_pDSBCapture is a pointer to the IDirectSoundCaptureBuffer8 interface of the capture buffer.

  • g_dwNextCaptureOffset tracks the buffer offset of the next block of data that will be copied to the file.

  • g_dwCaptureBufferSize is the size of the capture buffer, used in calculating wraparound.

    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 == g_pDSBCapture)
          return S_FALSE;
      if (NULL == g_pWaveFile)
          return S_FALSE;
     
      if (FAILED (hr = g_pDSBCapture->GetCurrentPosition( 
        NULL, &dwReadPos)))
          return hr;
     
      // Lock everything between the private cursor 
      // and the read cursor, allowing for wraparound.
     
      lLockSize = dwReadPos - g_dwNextCaptureOffset;
      if( lLockSize < 0 ) lLockSize += g_dwCaptureBufferSize;
     
      if( lLockSize == 0 ) return S_FALSE;
     
      if (FAILED(hr = g_pDSBCapture->Lock( 
            g_dwNextCaptureOffset, lLockSize, 
            &pbCaptureData, &dwCaptureLength, 
            &pbCaptureData2, &dwCaptureLength2, 0L)))
        return hr;
     
      // Write the data. This is done in two steps
      // to account for wraparound.
    
      if (FAILED( hr = g_pWaveFile->Write( dwCaptureLength, 
          (BYTE*)pbCaptureData, &dwDataWrote)))
        return hr; 
     
      if (pbCaptureData2 != NULL)
      {
        if (FAILED(hr = g_pWaveFile->Write( 
            dwCaptureLength2, (BYTE*)pbCaptureData2, 
            &dwDataWrote)))
          return hr; 
      }
     
      // Unlock the capture buffer.
     
     g_pDSBCapture->Unlock( pbCaptureData, dwCaptureLength, 
        pbCaptureData2, dwCaptureLength2  );
      
      // Move the capture offset forward.
     
      g_dwNextCaptureOffset += dwCaptureLength; 
      g_dwNextCaptureOffset %= g_dwCaptureBufferSize; 
      g_dwNextCaptureOffset += dwCaptureLength2; 
      g_dwNextCaptureOffset %= g_dwCaptureBufferSize; 
     
      return S_OK;
    }

When capturing is finished, the application closes the WAV file.

    g_pWaveFile->Close();