DSUTIL.CPP


DSUTIL .CPP

The dsutil.cpp utility file is both invaluable and flawed. In the following chapters, we will be changing, removing, and adding methods to our own version of this file. However, it is instructive to summarize here what this file has to offer.

CSoundManager Class

The CSoundManager class is well-named, because you will use it to manage the sound in your application. The only data item is a pointer to a DirectSound8 object. Like all good managers, this object handles initialization, termination, and coordination issues. The definition of the class follows .

 class CSoundManager  {  protected:      LPDIRECTSOUND8 m_pDS;  public:      CSoundManager();      ~CSoundManager();      HRESULT Initialize( HWND hWnd, DWORD dwCoopLevel );      inline  LPDIRECTSOUND8 GetDirectSound() { return m_pDS; }      HRESULT SetPrimaryBufferFormat( DWORD dwPrimaryChannels,                                      DWORD dwPrimaryFreq,                                      DWORD dwPrimaryBitRate );      HRESULT Get3DListenerInterface( LPDIRECTSOUND3DLISTENER* ppDSListener );      HRESULT Create( CSound** ppSound, LPTSTR strWaveFileName,                      DWORD dwCreationFlags = 0,                      GUID guid3DAlgorithm = GUID_NULL,                      DWORD dwNumBuffers = 1 );      HRESULT CreateFromMemory( CSound** ppSound, BYTE* pbData,                                ULONG ulDataSize, LPWAVEFORMATEX pwfx,                                DWORD dwCreationFlags = 0,                                GUID guid3DAlgorithm = GUID_NULL,                                DWORD dwNumBuffers = 1 );      HRESULT CreateStreaming( CStreamingSound** ppStreamingSound,                               LPTSTR strWaveFileName, DWORD dwCreationFlags,                               GUID guid3DAlgorithm, DWORD dwNotifyCount,                               DWORD dwNotifySize, HANDLE hNotifyEvent );  }; 

The constructor simply sets the DirectSound8 pointer to NULL , and the destructor cleanly destroys the object and will be called once at the end of your application, through the closeDirectSound function in our samples. We have already come across the Initialize and SetPrimaryBufferFormat methods, but we list them again here for completeness.

Initialize

 HRESULT Initialize( HWND hWnd, DWORD dwCoopLevel ); 

See the previous description of the initDirectSound function for an explanation of this method.

LPDIRECTSOUND8

 inline LPDIRECTSOUND8 GetDirectSound() { return m_pDS; } 

This inline method returns the pointer to the DirectSound object. You will probably not need to call this method.

SetPrimaryBufferFormat

 HRESULT SetPrimaryBufferFormat( DWORD dwPrimaryChannels,                                  DWORD dwPrimaryFreq,                                  DWORD dwPrimaryBitRate ); 

Although it is largely redundant, you will see this method called in many DirectSound samples. The method calls through to a DirectSound SDK method, SetFormat , which is used to set the format of the primary buffer.

Calling IDirectSoundBuffer8::SetFormat on the primary buffer has no effect on Windows Driver Model (WDM) drivers, except in applications that write to the primary buffer. If the application calls SetCooperativeLevel( DSSCL_WRITEPRIMARY ) , the primary buffer is actually used to play audio data, and SetFormat has the effect of immediately changing the format of that data (that is, the format in which DirectSound interprets it to be). However, very few applications use this mode, and on WDM drivers there s no benefit in using it.

WDM drivers were introduced with Windows 98 SE, although it is up to the audio card manufacturers, rather than Microsoft, as to whether they use this model rather than the earlier VxD driver model. If your applications target Windows 98, then you should add a call to SetPrimaryBufferFormat when initializing DirectSound. For 16-bit stereo sound, recorded at 22 KHz, that call would be as follows.

 hr = g_pSoundManager->SetPrimaryBufferFormat( 2, 22050, 16 ) 

Create

 HRESULT Create( CSound** ppSound, LPTSTR strWaveFileName,                  DWORD dwCreationFlags = 0,                  GUID guid3DAlgorithm = GUID_NULL,                  DWORD dwNumBuffers = 1 ); 

This is one of the most useful and often-used methods of this class, as it loads a sound from a wave file and duplicates the sound into buffers the required number of times. See the previous description of the loadSound function for further explanation of this method. Although there are defaults set for the last two parameters for this method, they will not always be what you want, so many times you will want to enter all five parameters. In Chapter 3, you will find an explanation of the guid3DAlgorithm parameter.

CreateFromMemory

 HRESULT CreateFromMemory( CSound** ppSound, BYTE* pbData,                            ULONG ulDataSize, LPWAVEFORMATEX pwfx,                            DWORD dwCreationFlags = 0,                            GUID guid3DAlgorithm = GUID_NULL,                            DWORD dwNumBuffers = 1 ); 

Calling this method will create a CSound object from a waveform held in a buffer. This method is very similar to the previous one, but used far less frequently. The only three parameters that are different from the Create method are the pointer to the buffer pbData , the length of the buffer ulDataSize , and a pointer to a WAVEFORMATEX structure describing this data.

This method is not used in any samples in this book.

CreateStreaming

 HRESULT CreateStreaming( CStreamingSound** ppStreamingSound,                           LPTSTR strWaveFileName, DWORD dwCreationFlags,                           GUID guid3DAlgorithm, DWORD dwNotifyCount,                           DWORD dwNotifySize, HANDLE hNotifyEvent ); 

This method creates a CStreamingSound object for a wave file. The advantage of streaming is that it allows a relatively small physical buffer to handle a much larger wave file. This can be very helpful, for example, when playing long music files. The disadvantage of using CStreamingSound over CSound objects is that there is an added level of complexity in handling the streams. See Chapter 6 for a description and working sample.

CSound Class

Unless your application is as simple as the High5 sample, you will need multiple copies of the object created by this class. A CSound object is used to process and play one or more instances of a sound. This is the class that you will need to modify the most for your own application, as we will show in the following chapters. The unmodified declaration of CSound follows.

 class CSound  {  protected:      LPDIRECTSOUNDBUFFER* m_apDSBuffer;      DWORD                m_dwDSBufferSize;      CWaveFile*           m_pWaveFile;      DWORD                m_dwNumBuffers;      DWORD                m_dwCreationFlags;      HRESULT RestoreBuffer( LPDIRECTSOUNDBUFFER pDSB, BOOL* pbWasRestored );   public:      CSound( LPDIRECTSOUNDBUFFER* apDSBuffer, DWORD dwDSBufferSize,              DWORD dwNumBuffers, CWaveFile* pWaveFile, DWORD dwCreationFlags );      virtual ~CSound();      HRESULT Get3DBufferInterface( DWORD dwIndex,                                    LPDIRECTSOUND3DBUFFER* ppDS3DBuffer );      HRESULT FillBufferWithSound( LPDIRECTSOUNDBUFFER pDSB,                                   BOOL bRepeatWavIfBufferLarger );      LPDIRECTSOUNDBUFFER GetFreeBuffer();      LPDIRECTSOUNDBUFFER GetBuffer( DWORD dwIndex );      HRESULT Play( DWORD dwPriority = 0, DWORD dwFlags = 0,                    LONG lVolume = 0, LONG lFrequency = -1, LONG lPan = 0 );      HRESULT Play3D( LPDS3DBUFFER p3DBuffer, DWORD dwPriority = 0,                      DWORD dwFlags = 0, LONG lFrequency = 0 );      HRESULT Stop();      HRESULT Reset();      BOOL    IsSoundPlaying();  }; 

The creation function for the CSound class is called by the Create method of the CSoundManager class. Although you will probably not need to call the creation function, you will need to modify it later to handle special effects. The destructor will be called through our closeDirectSound call.

Get3DBufferInterface

 HRESULT Get3DBufferInterface( DWORD dwIndex,                                LPDIRECTSOUND3DBUFFER* ppDS3DBuffer ); 

This method retrieves the 3-D buffer interface associated with one particular buffer for this sound. If you created, for example, a sound with four sound buffers, then dwIndex can be 0 through 3 to return one of up to four associated 3-D buffers.

The way that DirectSound handles 3-D sound is that it associates an additional 3-D buffer description with the 2-D sound held in the CSound object. This particular method is not used in later samples, because we integrate the 3-D buffer description into the CSound class.

FillBufferWithSound

 HRESULT FillBufferWithSound( LPDIRECTSOUNDBUFFER pDSB,                               BOOL bRepeatWavIfBufferLarger ); 

This is a very useful method, although it is encapsulated in calls from this and other utility classes. Its purpose is to fill a buffer with the sound that was provided to the class constructor. If the buffer is larger than the provided sound, the remainder will be filled with silence unless you set the bRepeatWavIfBufferLarger flag to True, in which case the sound will be repeated until the buffer is full (although none of the calls in the utility code do this). You will probably not need to call this method directly, nor modify it.

GetFreeBuffer

 LPDIRECTSOUNDBUFFER GetFreeBuffer(); 

This method finds a buffer that is not playing; if it cannot find one randomly , it stops a buffer and selects it. This is not a useful method for building applications, and needs to be replaced (see Chapter 2).

GetBuffer

 LPDIRECTSOUNDBUFFER GetBuffer( DWORD dwIndex ); 

This is a simple method that retrieves a pointer to a buffer, if the index is valid. You will probably not need to modify this method.

Play

 HRESULT Play( DWORD dwPriority = 0, DWORD dwFlags = 0,                LONG lVolume = 0, LONG lFrequency = -1, LONG lPan = 0 ); 

This method plays a buffer, but uses the unreliable GetFreeBuffer method rather than allowing the application to select which buffer to play, so this is another method that needs modified. However, this method is useful for discussing the parameters. Volume parameters sent to DirectSound are in the range -100.00 decibels to 0 decibels, which is the attenuation (the reduction in volume) applied to the volume of the wave file. Volume parameters are supplied as 100ths of a decibel in a long variable (so a value of -150 means the original volume is reduced by -1.5 decibels). Frequencies are supplied to DirectSound in hertz, and it is possible to play a sound at a different frequency than it was recorded. Panning values also use the 100th of a decibel unit, and record the attenuation for the left and right speakers . These values are discussed in more detail in Chapters 2 and 3, but the following code shows the defined minimums and maximums (note the increase in maximum frequency supported by DirectSound version 9 or later).

 #define DSBFREQUENCY_MIN            100  #if DIRECTSOUND_VERSION >= 0x0900      #define DSBFREQUENCY_MAX            200000  #else      #define DSBFREQUENCY_MAX            100000  #endif  #define DSBPAN_LEFT                 -10000  #define DSBPAN_CENTER               0  #define DSBPAN_RIGHT                10000  #define DSBVOLUME_MIN               -10000  #define DSBVOLUME_MAX               0. 

Play3D

 HRESULT Play3D( LPDS3DBUFFER p3DBuffer, DWORD dwPriority = 0,                  DWORD dwFlags = 0, LONG lFrequency = 0 ); 

This is the 3-D version of the previous method. It is helpful in showing how to associate a 3-D buffer with the playing of a sound, but is not a method that should be used without modification (see Chapter 3).

Stop

 HRESULT Stop(); 

This method stops all the buffers of the sound. You may sometimes want to do this, however, it is usually more useful to stop individual buffers (see Chapter 2). Stopping sounds also involves deactivating special effects (see Chapter 4).

Reset

 HRESULT Reset(); 

For each sound buffer, there is a pointer that stores where the sound is currently being played from. This method resets all the pointers for all sound buffers back to the beginning. Similar to the previous method, we will need to add a new method that resets the pointer of one specified buffer (see Chapter 2).

IsSoundPlaying

 BOOL IsSoundPlaying(); 

This method returns True if any sound buffers are playing. Again, a more useful version of this method would be one that determines whether a particular sound buffer is playing or not.

CStreamingSound Class

The CStreamingSound class inherits from the CSound class, and adds just two methods in addition to the constructor and destructor. A CStreamingSound object manages a single streaming buffer, so you cannot create duplicate buffers of the wave file. If you wish to stream multiple copies of the same sound, then you will need to create multiple CStreamingSound objects. However, typically it is used to manage large files, such as background music, where having multiple buffers is usually not required (see Chapter 6).

 class CStreamingSound : public CSound  {  protected:      DWORD m_dwLastPlayPos;      DWORD m_dwPlayProgress;      DWORD m_dwNotifySize;      DWORD m_dwNextWriteOffset;      BOOL  m_bFillNextNotificationWithSilence;  public:      CStreamingSound( LPDIRECTSOUNDBUFFER pDSBuffer, DWORD dwDSBufferSize,                       CWaveFile* pWaveFile, DWORD dwNotifySize );      ~CStreamingSound();      HRESULT HandleWaveStreamNotification( BOOL bLoopedPlay );      HRESULT Reset();  }; 

Create a CStreamingSound object by calling the CreateStreaming method of the CSoundManager class. The constructor of the CStreamingSound object will call the constructor of the inherited CSound object, and will initialize it appropriately.

The parameters of the constructor are, in order, a pointer to a DirectSound buffer, the size of that buffer, the file name of the wave file, and the notification size. Only the last parameter requires much explanation. After that number of bytes has been played, a notification message ( WAIT_OBJECT_0 ) will be sent and picked up by a callback function. Respond with a call to HandleWaveStreamNotification .

Note that the notification callback is handled in a separate thread, and not in the Windows callback function. This complexity is explained in detail in Chapter 6.

HandleWaveStreamNotification

 HRESULT HandleWaveStreamNotification( BOOL bLoopedPlay ); 

This method reads data from the wave file, and writes it to the section of the streaming buffer that has just been played. If the end of the data is reached, then the remainder of the buffer will be filled with either silence ( bLoopedPlay is False) or by reading from the beginning of the file again ( bLoopedPlay is True).

Reset

 HRESULT Reset(); 

This method resets the streaming buffer to start reading from the beginning of the file.

CWaveFile Class

The CWaveFile class saves a developer the grief of having to write a wave file parser. Although all of the methods are usually called from the previously mentioned classes, occasionally you might need to call the GetFormat method to extract some information about the file when you are extending those classes. The CWaveFile class provides methods to control both the reading and writing of wave files. We have kept the explanations to a minimum, because we do not recommend amending this class. It works just fine.

 class CWaveFile  {  public:      WAVEFORMATEX* m_pwfx;        // Pointer to WAVEFORMATEX structure.      HMMIO         m_hmmio;       // Multimedia I/O handle for the wave.      MMCKINFO      m_ck;          // Multimedia RIFF chunk.      MMCKINFO      m_ckRiff;      // Use in opening a wave file.      DWORD         m_dwSize;      // The size of the wave file.      MMIOINFO      m_mmioinfoOut;      DWORD         m_dwFlags;      BOOL          m_bIsReadingFromMemory;      BYTE*         m_pbData;      BYTE*         m_pbDataCur;      ULONG         m_ulDataSize;      CHAR*         m_pResourceBuffer;  protected:      HRESULT ReadMMIO();      HRESULT WriteMMIO( WAVEFORMATEX *pwfxDest );  public:      CWaveFile();      ~CWaveFile();      HRESULT Open( LPTSTR strFileName, WAVEFORMATEX* pwfx, DWORD dwFlags );      HRESULT OpenFromMemory( BYTE* pbData, ULONG ulDataSize,                              WAVEFORMATEX* pwfx, DWORD dwFlags );      HRESULT Close();      HRESULT Read( BYTE* pBuffer, DWORD dwSizeToRead, DWORD* pdwSizeRead );      HRESULT Write( UINT nSizeToWrite, BYTE* pbData, UINT* pnSizeWrote );      DWORD   GetSize();      HRESULT ResetFile();      WAVEFORMATEX* GetFormat() { return m_pwfx; };  }; 

Open

 HRESULT Open( LPTSTR strFileName, WAVEFORMATEX* pwfx, DWORD dwFlags ); 

When reading from a file or resource, this method returns the wave format and the data chunk size. The strFileName parameter should be set to the file name or resource name. The pwfx parameter should point to a WAVEFORMATEX structure, and dwFlags should be set to WAVEFILE_READ .

When writing to a file, fill out the WAVEFORMATEX structure with the required format information, and set dwFlags to WAVEFILE_WRITE .

OpenFromMemory

 HRESULT OpenFromMemory( BYTE* pbData, ULONG ulDataSize,                          WAVEFORMATEX* pwfx, DWORD dwFlags ); 

This method reads wave data from memory rather than from a file. We have not used this method in any of the samples in this book.

Close

 HRESULT Close(); 

This method closes the file, after writing out the RIFF chunk sizes if the file was open for writing.

Read

 HRESULT Read( BYTE* pBuffer, DWORD dwSizeToRead, DWORD* pdwSizeRead ); 

This method reads data from the wave file, resource or memory location.

Write

 HRESULT Write( UINT nSizeToWrite, BYTE* pbData, UINT* pnSizeWrote ); 

This method writes data out to the file from the given address.

GetSize

 DWORD   GetSize(); 

This method will return the size of an opened wave file, in bytes.

ResetFile

 HRESULT ResetFile(); 

This method resets the pointer used for playing the file back to the beginning.

GetFormat

 WAVEFORMATEX* GetFormat(); 

This method returns the address of the WAVEFORMATEX structure used to describe the wave format.




Fundamentals of Audio and Video Programming for Games
Fundamentals of Audio and Video Programming for Games (Pro-Developer)
ISBN: 073561945X
EAN: 2147483647
Year: 2003
Pages: 120

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net