Writing to a WAV File

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 used by many of the SDK sample applications implements a CWaveFile class that has the following methods for managing capture files:

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:

    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();

See Also