Platform SDK: DirectX

Writing to a Wave File

[Visual Basic]

Short sounds can be saved from a secondary buffer to a wave file by using DirectSoundBuffer.SaveToFile. This method copies the entire contents of the buffer to a file.

In most cases, the sound that you want to save starts out in a capture buffer. The following code shows how to move data from a capture buffer, dscb, to a secondary buffer, from where it can be saved to file.

Dim ByteBuffer() As Integer 
Dim dsc As DirectSoundCapture 
Dim dscd As DSCBUFFERDESC
Dim dsd As DSBUFFERDESC
Dim capCURS As DSCURSORS
.
.
.
' Assume that dscd has been initialized with the format of the
' capture buffer and that dsb describes a secondary buffer of
' equal size.
 
' Create a secondary buffer with the same format 
' as the capture buffer.
 
Set dsb = ds.CreateSoundBuffer(dsd, dscd.fxFormat)
 
' Make a private buffer large enough to hold the contents of the
' capture buffer up to the write cursor.
 
dscb.GetCurrentPosition capCURS
ReDim ByteBuffer(capCURS.lWrite + 1)
 
' Read the contents of the capture buffer into the private buffer.
 
dscb.ReadBuffer 0, capCURS.lWrite * dscd.fxFormat.nBlockAlign, _
        ByteBuffer(0), DSCBLOCK_DEFAULT
 
' Write the private buffer to the secondary buffer.
 
dsb.WriteBuffer 0, capCURS.lWrite, ByteBuffer(0), DSBLOCK_DEFAULT

For sounds that might exceed the length of the capture buffer, you need to create the wave file yourself and stream data into it. Streaming captured data to a wave file is done in the following steps:

  1. Create the file and write the file header, the format chunk, and the header of the data chunk. The format chunk must describe the format of the capture buffer, which can be obtained by using DirectSoundCaptureBuffer.GetFormat. Note, however, that the elements of the format are not in the same order as in the WAVEFORMATEX type. For information on the organization of headers and chunks, see Reading from a Wave File.
  2. Stream data from a capture buffer to a private buffer and from there to the file, keeping track of how many bytes have been written. For more information, see Using the Capture Buffer.
  3. When capture stops and all data has been written, calculate the length of the data chunk and the total size of the file, and write these values to the appropriate locations in the data chunk header and file header respectively.
[C++]

To prepare for writing to a wave file, the application must first declare four variables to be passed to the functions in Wavwrite.cpp:

WAVEFORMATEX  wfx;            // Wave format info
HMMIO         hmmio;          // File handle
MMCKINFO      mmckinfoData;   // Chunk info
MMCKINFO      mmckinfoParent; // Parent chunk (file) info
 

You must also initialize the WAVEFORMATEX structure with the format of the capture buffer.

Now you call the WaveOpenFile function, passing in the desired filename and the addresses of the global variables. The function creates the file and writes some header information. Like other functions in Wavwrite.cpp, WaveOpenFile returns zero if successful.

if (WaveOpenFile(pszFileName, &hmmio, &wfx, 
        &mmckinfoData, &mmckinfoParent))
{
    // Failure
}
 

Next, call the WaveStartDataWrite function, which initializes the data chunk.

if (WaveStartDataWrite(&hmmio, &mmckinfoData, &mmioinfo))
{
     // Failure
}
 

The file is now ready to receive data. The following fragment illustrates how data might be copied from a capture buffer to a file.

/* It is assumed that the following variables contain
   valid assignments:
LPDIRECTSOUNDCAPTUREBUFFER lpdscb;    // Capture buffer
DSCBUFFERDESC    dscbDesc;            // Capture buffer description
DWORD            dwReadCursor;        // Internal cursor in buffer
DWORD            dwNumBytes;          // Bytes available
DWORD            dwTotalBytesWritten; // Running total in file
*/
 
LPBYTE pbInput1, pbInput2; // Pointers to data in buffer
DWORD  cbInput1, cbInput2; // Count of bytes in locked portion
UINT   BytesWritten;       // Count of bytes written to file
 
if FAILED(hr = lpdscb->Lock(dwReadCursor, dwNumBytes,
                      (LPVOID *)&pbInput1, &cbInput1, 
                      (LPVOID *)&pbInput2, &cbInput2, 0))
{
    // Failure
}
else
{
    if (WaveWriteFile(hmmio, cbInput1, pbInput1, &mmckinfoData,
                    &dwBytesWritten, &mmioinfo))
    {
        // Failure
    }
    else dwTotalBytesWritten += BytesWritten; 
    if (pbInput2 != NULL) 
    {
        if (WaveWriteFile(hmmio, cbInput2, pbInput2, &mmckinfoData,
                    &BytesWritten, &mmioinfo))
        {
            // Failure
        }
        else dwTotalBytesWritten += BytesWritten; 
 
    }
    lpdscb->Unlock(pbInput1, cbInput1, pbInput2, cbInput2);
 
    // Increment internal cursor, compensating for wrap around
    dwReadCursor += dwNumBytes;
    while (dwReadCursor >= dscbDesc.dwBufferBytes)
                dwMyReadCursor -= dscbDesc.dwBufferBytes;
 
}
 

When you are finished capturing data, you close the file:

WaveCloseWriteFile(&hmmio, &mmckinfoData, 
        &mmckinfoParent, &mmioinfo,
        dwTotalBytesWritten / (wfx.wBitsPerSample / 8));
 

The WaveCloseWriteFile function calculates the total number of samples in the file and writes this number to the data chunk header.