Microsoft DirectX 8.1 (C++)

Writing to a Wave File

The DirectSound API does not include methods for writing to wave files. However, the Dsutil.cpp file used by many of the SDK sample applications implements a CWaveFile class that has the following public methods:

The first step in writing a wave file is to call the CWaveFile::Open method. This creates the file and writes the wave 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 wave 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 function is called each time the read cursor reaches a notification position. 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 our 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 along.
 
  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 wave file.

g_pWaveFile->Close();

The CWaveFile::Close method writes the chunk sizes to the file before closing it.