Microsoft DirectX 8.1 (Visual Basic)

Reading Wave Files

This topic explains how to load data from wave files into DirectSound buffers without the help of the DirectMusic loader and performance. Most applications do not need to work directly with wave data and buffers. For the preferred method of loading and playing waves, see Loading Audio Data and Playing Sounds.

For short sounds, the easiest way to play a wave file using DirectSound alone is to load it into a static buffer by calling the DirectSound8.CreateSoundBufferFromFile method. See Creating Secondary Buffers.

For larger files that will not conveniently fit in memory, you need to create a streaming buffer and read the file in pieces.

Wave files are in the Resource Interchange File Format (RIFF), which consists of a file header followed by variable number of "chunks," each made up of a header and data. The chunk header consists of a four-character tag identifying the type of data and a Long giving the length of the data.

The wave file header is organized as follows:

The first chunk is always the format chunk, which looks like this:

The PCM sample data in a wave file is contained in a chunk beginning with the string "data", which can also be read as the Long &H61746164. Most often this chunk immediately follows the format chunk, but because RIFF is an extensible format, there is no guarantee that some other type of chunk will not precede it. Your file parser must be capable of ignoring chunks it cannot handle.

To parse a wave file, it is helpful to have three user-defined types. The first will receive all the information in the file header and in the header of the format chunk.

Private Type FileHeader
  lRiff As Long
  lFileSize As Long
  lWave As Long
  lFormat As Long
  lFormatLength As Long
End Type

The second type will receive the format data. You can't use the WAVEFORMATEX type for this, because the members are in a different order. Your user-defined type needs to retrieve only 16 bytes, because if WAVEFORMATEX.nSize is present, it is always zero in standard PCM files.

Private Type WaveFormat
  wFormatTag As Integer
  nChannels As Integer
  nSamplesPerSec As Long
  nAvgBytesPerSec As Long
  nBlockAlign As Integer
  wBitsPerSample As Integer
End Type

The third type can be used to retrieve the header of any chunk, including the data chunk.

Private Type ChunkHeader 
  lType As Long
  lLen As Long
End Type

The following sample function verifies that a file is a RIFF wave file, seeks the beginning of the sample data, and returns a WAVEFORMATEX type containing information about the wave format:

Dim FileFree As Long     ' Global file handle.
Dim lDataLength As Long    ' Global data length.

Private Function FillFormat(FileName As String) As WAVEFORMATEX
 
  Dim Header As FileHeader
  Dim HdrFormat As WaveFormat
  Dim chunk As ChunkHeader
  Dim by As Byte
  Dim i As Long
  
  ' Open the file and read the header.
 
  Close #FileFree
  FileFree = FreeFile 
  Open FileName For Binary Access Read As #FileFree
  Get #FileFree, , Header 
 
  ' Check for "RIFF" tag and exit if not found.
 
  If Header.lRiff <> &H46464952 Then 
    Exit Function
  End If
 
  ' Check for "WAVE" tag and exit if not found.
 
  If Header.lWave <> &H45564157 Then 
    Exit Function
  End If
 
  ' Check format chunk length; if less than 16, 
  ' it's not PCM data so we can't use it.
 
  If Header.lFormatLength < 16 Then
    Exit Function
  End If
  
  ' Retrieve format.
 
  Get #FileFree, , HdrFormat
 
  ' Seek to next chunk by discarding any extra WAVEFORMATEX bytes.
 
  For i = 1 To Header.lFormatLength - 16
    Get #FileFree, , by
  Next
 
  ' Ignore chunks until we get to the "data" chunk.
 
  Get #FileFree, , chunk
  Do While chunk.lType <> &H61746164
    For i = 1 To chunk.lLen
    Get #FileFree, , by
    Next
    Get #FileFree, , chunk
  Loop
 
  ' Retrieve the size of the data.
 
  lDataLength = chunk.lLen
 
 ' Fill the returned type with the format information.

  With FillFormat 
    .lAvgBytesPerSec = HdrFormat.nAvgBytesPerSec
    .lExtra = 0
    .lSamplesPerSec = HdrFormat.nSamplesPerSec
    .nBitsPerSample = HdrFormat.wBitsPerSample
    .nBlockAlign = HdrFormat.nBlockAlign
    .nChannels = HdrFormat.nChannels
    .nFormatTag = HdrFormat.wFormatTag
    .nSize = 0
  End With
 
End Function

The application can now begin reading data from the file and streaming that data into the secondary sound buffer. It's impossible to read data directly from the file into the secondary buffer, so you must first read the data into a private buffer and then copy it to the secondary buffer by using DirectSoundSecondaryBuffer8.WriteBuffer.

For more information on streaming, see Using Streaming Buffers and Play Buffer Notification.