Search Tips Advanced Search |
| Delphi Graphics and Game Programming Exposed with DirectX 7.0 by John Ayres Wordware Publishing, Inc. ISBN: 1556226373 Pub Date: 12/01/99
Search this book:
Previous | Table of Contents | Next
Listing 9-7: Creating a streaming buffer
    var

      DXSound: IDirectSound;

      Sound: IDirectSoundBuffer;

      WaveFormat: PWaveFormatEx;

      mmioHndle: HMMIO;

      ChunkInfo,

      ChunkInfoParent: TMMCKInfo;

    procedure TForm1.FormCreate(Sender: TObject);

    var

      Audio: PByte;

      Junk: Pointer;

      NumBytesLocked: DWORD;

      JunkBytes: DWORD;

      BufDesc: TDSBufferDesc;

    begin

      {the OnIdle event serves as our game loop as it would in

       DirectDraw examples}

      Application.OnIdle := AppIdle;

      {create the DirectSound object}

      DirectSoundCreate(nil, DXSound, nil);

      {set its cooperative level (we won’t change the primary buffer format, so

       a cooperative level of Normal is all that is needed)}

      DXSound.SetCooperativeLevel(Handle, DSSCL_NORMAL);

      {open the specified WAV file and read its format information}

      WaveOpenFile(PChar(ExtractFilePath(ParamStr(0))+‘longwave.wav’), mmioHndle,

                   WaveFormat, @ChunkInfoParent);

      {advance the file pointer to the beginning of audio data, and retrieve the

       size of the buffer required to hold all of the audio data}

      WaveStartDataRead(mmioHndle, @ChunkInfo, @ChunkInfoParent);

      {initialize a buffer description record, including the size of the audio

       data and its format retrieved from the previous two function calls}

      FillMemory(@BufDesc, SizeOf(TDSBufferDesc), 0);

      BufDesc.dwSize := SizeOf(TDSBufferDesc);

      BufDesc.dwFlags := DSBCAPS_GETCURRENTPOSITION2 or DSBCAPS_LOCSOFTWARE;

      BufDesc.dwBufferBytes := WaveFormat.nAvgBytesPerSec*2;   // 2 seconds worth

      BufDesc.lpwfxFormat := WaveFormat;

      {create the sound buffer object}

      DXSound.CreateSoundBuffer(BufDesc, Sound, nil);

      {initialize it with silence}

      FillBufferWithSilence(Sound);

      {begin playing the buffer}

      Sound.Play(0, 0, DSBPLAY_LOOPING);

    end;

    procedure TForm1.FormDestroy(Sender: TObject);

    begin

      {disconnect our loop}

      Application.OnIdle := nil;

      {we no longer need to access the WAV file, so close it}

      WaveCloseReadFile(mmioHndle, WaveFormat);

    end;

    procedure TForm1.AppIdle(Sender: TObject; var Done: Boolean);

    const

      LastPlayPos: Integer = 0;

    var

      CurPlayPos,

      CurWritePos: Integer;

      StartOffset: Integer;

      Audio,

      Junk: Pointer;

      NumBytesLocked,

      JunkBytes,

      BytesRead: DWORD;

    begin

      {setting Done to FALSE indicates that we want the application to

       continuously call this function}

      Done := FALSE;

      {if the DirectSound object has been created...}

      if Assigned(Sound) then

      begin

        {retrieve the play and write positions}

        Sound.GetCurrentPosition(@CurPlayPos, @CurWritePos);

        {since our buffer is only 2 seconds long, WaveFormat.nAvgBytesPerSec

         represents the midpoint of the buffer (1 second)}

        if (CurPlayPos<LastPlayPos) or ((CurPlayPos>=WaveFormat.nAvgBytesPerSec)

        and (LastPlayPos<WaveFormat.nAvgBytesPerSec)) then

        begin

          {wrap around the starting offset as necessary}

          If CurPlayPos>=WaveFormat.nAvgBytesPerSec then

            StartOffset := 0

          else

            StartOffset := WaveFormat.nAvgBytesPerSec;

          {lock the buffer, preparing it to write out 1 second of data}

          Sound.Lock(StartOffset, WaveFormat.nAvgBytesPerSec, Pointer(Audio),

                     NumBytesLocked, Junk, JunkBytes, 0);

          {copy the audio data into the sound buffer object}

          WaveReadFile(mmioHndle, NumBytesLocked, Audio, @ChunkInfo, BytesRead);

          {copy any extra bytes as necessary}

          if BytesRead<NumBytesLocked then

          begin

            WaveStartDataRead(mmioHndle, @ChunkInfo, @ChunkInfoParent);

            WaveReadFile(mmioHndle, NumBytesLocked-BytesRead,

                         Pointer(Longint(Audio)+BytesRead), @ChunkInfo, BytesRead);

          end;

          {unlock the buffer}

          Sound.Unlock(Audio, NumBytesLocked, nil, 0);

          {store the last known playing position}

          LastPlayPos := CurPlayPos;

        end;

      end;

    end;
Duplicating Buffers It is quite common in action games to have a short sound that is repeated often, such as gunshots, laser bursts, or even explosions. To play a regular static buffer in its entirety, the SetCurrentPosition method is called to “rewind” the sound, and then the Play method is called to begin audio output. If this sound is to be repeated rapidly, such as when the user is frantically pressing the fire button, the sound may seem to stutter, as each time the sound is replayed the previously playing sound is started from the beginning. The solution would be some method by which this sound buffer can be copied, sharing the sound data with the original buffer but with its own playing position. Fortunately, DirectSound provides a method by which a static buffer can be duplicated. By calling the DuplicateSoundBuffer method, another IDirectSoundBuffer object is created. This new object has its own play and write pointers, but it shares the audio data with the original sound (as opposed to creating its own sound buffer). It can be played or stopped regardless of the state of the original sound, but since it shares the original buffer, a duplicated buffer should not be locked nor should its data be modified. The DuplicateSoundBuffer method is defined as:
    function DuplicateSoundBuffer(

    lpDsbOriginal: IDirectSoundBuffer;        // the original sound buffer

    var lpDsbDuplicate: IDirectSoundBuffer    // the duplicated sound buffer

    ): HResult;                               // returns a DirectX error code
The first parameter is a pointer to the sound buffer that is to be duplicated. The second parameter is an IDirectSoundBuffer object that is instantiated as a copy of the original when the function returns. Bear in mind that although the sound buffer is not reproduced, duplicating a sound buffer does require some resources. Thus, it is better to locate static buffers to be duplicated in system memory, as duplicating a hardware buffer may require additional hardware resources that are not available or should be used for more efficient purposes.
| Note: Calling DuplicateSoundBuffer on a hardware buffer will fail when no more (or insufficient) hardware resources are available.
The DirectX SDK comes with an object that encapsulates this buffer duplication, making it easy to manage sounds that must be played multiple times concurrently. While the following example makes no use of any such object, it is easy to see how one could potentially be created to encapsulate the functionality as illustrated. The example demonstrates how to play a single sound multiple times by using the DuplicateSoundBuffer method to create several copies of the sound. For comparison purposes, the original sound can be played as well, illustrating how using the DuplicateSoundBuffer method can help eliminate stutter from sounds played in rapid succession.
Previous | Table of Contents | Next

Products | Contact Us | About Us | Privacy | Ad Info | Home Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.