asdDsLib.txt - the read-me file for the DsLib library.

Created by: Andrew S. Dunsmore
Date: 10/03/04
Modified: 02/27/05

v1.2 updates from v1.0: The code, along with dsound.lib and winmm.lib, has been compiled into a library. This makes using the library easier, but source code is no longer available for learning or modifying the code. pcmSoundType was changed from a DWORD to an enumeration and the #define values were removed. The functions ParseWaveMemory(), MemoryNew(), and MemoryDelete() were moved into the lib file. This was done to make learning the code easier, as these functions don't need to be called directly by the user.

The asdDSound Library is a library to make using DirectSound easier. The library takes care of declaring and initializing all required variables and it provides numerous functions to take care of the calls necessary for loading, manipulating, and playing sounds.

The library is currently only implemented to use wave files. I do not currently have any experience in using other formats of sound files (like .vox), so I didn't add them here. If I need to use any of the other formats in the future, I may update this library.

THE INCLUDE AND EXTERNAL FILES
The library has been built with the dsound.h library included in the header file, and dsound.lib and winmm.lib were compiled into the lib file. When using the library with your projects, all you need to do is add the lib file to your VC++ 6.0 project, or if you're using VC++.net, add asdDSound.lib to your additional input files found under project properties.

THE DEFINED VARIABLES
There are a number of defined variables in this library. They, and their uses are detailed as such:

#define DSBCAPS_CTRLDEFAULT (DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME)
	- this is required for backward compatability with DirectSound v7.0

#define MAX_SOUNDS	255		- This is the value used to define the array of sounds for your program.
					  If you don't need this many sounds or need more sounds, change this
					  value by redefining the value in your cpp file. If the value is too
					  large, you'll waste memory and deal with substantial overhead, as
					  DSoundInitialize() and DSoundShutdown() use this value in an O(n)
					  procedure. (For the amatures who haven't had any profession training,
					  this means that the procedure's length of processor usage is linear
					  based on the value n (n = 255 here). To make the explanation of this as
					  short and simple as possible, if you keep 255, the two functions listed
					  above will take about 255 clock cycles on your processor to complete,
					  but if you change the value to 5, the functions will only take about 5
					  clock cycles to complete.)

THE VARIABLES
The variables declared in the header file and their uses are:

typedef struct pcmSoundTyp			- this structure is defined to hold a single sound
{
	LPDIRECTSOUNDBUFFER	dsbuffer;	- the ds buffer containing the sound
	DWORD			playable;	- determines if the sound is invalid or to be played or not
} pcmSound, *pcmSoundPtr;			  (see the above explanation)

pcmSound		soundFx[MAX_SOUNDS];	- the array of sound buffers (see the explanation at MAX_SOUNDS)
LPDIRECTSOUND	lpds	= NULL;			- pointer to the DirectSound object
DSBUFFERDESC	dsbd;				- the DirectSound buffer description
WAVEFORMATEX	pcmwf;				- holds the wave format description

THE MACROS
#define DX_INIT_STRUCT(ddstruct) { ZeroMemory(&ddstruct, sizeof(ddstruct)); ddstruct.dwSize = sizeof(ddstruct); }
	- initializes a DirectX structure for data

#define MULTI_TO_WIDE(x, y) MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, x, -1, y, _MAX_PATH);
	- Convert from multibyte (standard) format to Unicode (wide) format

#define WIDE_TO_MULTI(x, y) WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, x, -1, y, _MAX_PATH, NULL, NULL);
	- Convert from Unicode (wide) format to multibyte (standard) format

#define DSVOLUME_TO_DB(volume) ((DWORD) (-30 * (100 - volume)))
	- Convert from a linear sound value to decibles

THE FUNCTIONS
- DirectSound
HRESULT	DSoundInitialize(HWND mainWindowHandle,
						 int flags = DSBCAPS_CTRLDEFAULT | DSBCAPS_STATIC | DSBCAPS_LOCSOFTWARE);
void	DSoundShutdown(void);

- Open/Load Wave files
bool	OpenWaveSound(int soundIndex, char* fileName);
bool	LoadWaveSound(HINSTANCE hInstanceApp, int soundIndex, int ID, char* group);

- Turn sounds on and off
DWORD	TurnSoundOn(int soundIndex);
DWORD	TurnSoundOff(int soundIndex);

- Change the sound's volume
HRESULT	SetMaxVolume(int soundIndex);
HRESULT	SetSoundVolume(int soundIndex, int volume);

- Chage the sound's frequency
HRESULT	SetSoundFreqStd(int soundIndex);
HRESULT	SetSoundFreq(int soundIndex, int frequency);

- Pan the sound
HRESULT PanSoundCenter(int soundIndex);
HRESULT	PanSoundLeft(int soundIndex);
HRESULT	PanSoundRight(int soundIndex);
HRESULT	PanSound(int soundIndex, int panValue);

- Play or stop the sound
HRESULT	PlaySound(int soundIndex);
void	StopSound(int soundIndex);

The two DirectSound functions need to be used if you are using DirectSound in your application. When to call these are pretty self-explanitory. Call DSoundInitialize() at the beginning of your application (or before you use any of the other functions), and call DSoundShutdown() at the end of your application (or after you're finished with all the other DirectSound functions).

One difficult thing about using these functions is the first parameter passed into DSoundInitialize() (and no, it's not THAT difficult). This parameter is filled by the handle to the window where you want DirectSound to control your keyboard data. The second parameter for this function contains the control flags that you are wanting your sounds to have. For a complete list of the possible flags look in dsound.h or the Microsoft DirectX SDK.

The return value for the DSoundInitialize() function is the DirectX return value that results from the DirectSound API calls used in this function. If everything works out, the function will return DS_OK, but if something fails, the return value will be the DirectX error code. See DSound.h for a list of all the possible error codes. An easy way to test if the function completes with no problems is to use the DirectX FAILED() function. See the driver program's code for an example of this.

The first of the five Load/Open sound functions, OpenWaveSound(), is used to open a wave file from disk. The first parameter of the function is the index where you want your sound stored in the soundFx[] array. The second parameter is the string describring the path and file you wish to open. The function will return true if it is successful in opening the file and false otherwise.

The second function is this group, LoadWaveSound(), is used to load a wave sound from a resource that has been added to your C++ project. The first parameter comes from the first argument passed into your WinMain() function. See the driver program for more information. The second parameter is the index where you want to load the sound into in the soundFx[] array. The third parameter is the id of the resource you wish to load, and the fourth parameter is the name of the group where the resource is located in your project.

A known problem with this library is the LoadWaveSound() function. When you loading a wave file from a resource, everything seems to be fine until you play the sound. When played, you hear the sound fine, but after the sound is over there's a blank sound at the end of it. You may hear a slight pop after the sound is finished playing. It's not very noticible unless you're really looking for it. If you've played my game, Super Pong, you may have noticed this when the ball hits an object, as all the sounds in my game are loaded from resources and use this function.

The sound volume functions are pretty self-explanitory. When a sound is loaded, the volume defaults to play at the maximum value of the volume on the user's speakers. Values for SetSoundVolume() must be between 0 (silent) and 100 (max). Calling SetSoundVolume() with a value of 100 is the same as calling SetMaxVolume().

The frequency functions are used to change the speed at which a sound is played back. Setting the value low will cause the sound to play slower and deeper, while raising the frequency will cause the sound to play faster and in a higher pitch. Typically, wave sounds are set to be played at 11,025 hz. Calling SetFreqStd() will set the sound's frequency to its normal frequency, even if this frequency isn't 11,025 hz.

The panning functions are used to pan the sound between the left and right speakers. The values for PanSound() can be between -10,000 and 10,000. Using the value of -10,000 will result in the sound being played all in the left speaker (similar to calling PanSoundLeft()), calling the function with a value of 10,000 will result in the sound being played only in the right speaker (similar to PanSoundRight()). Calling the function with a value of 0 will set the sound to be played equally on both speakers (as would SetSoundCenter()). The sound is played in the center when it is first loaded.

Finally, the PlaySound() function does exactly what you think it does, and StopSound() stops a sound that is playing.

EXAMPLE CODE
This section shows how to use the libary.

int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow)
{
  HWND hwnd;

  // fill in the values of your window class structure, to include the following call
  winclass.hInstance = hinstance;

  // register your window class and create your window class kinda like this
  hwnd = CreateWindowEx(/*your parameters*/);

  // now set up DirectSound
  if(FAILED(DSoundInitialize(hwnd)))
  {
    // error
    MessageBox("DirectSound failed to initialize"); // I know this isn't the way to call this, but know what I mean, right?
	return;
  }

  // if you're going to use sounds from files, load them like this
  if(FAILED(OpenWaveSound(0, "mySound.wav")))
  {
    MessageBox("Failed to open sound");
	return;
  }

  // or if you're going to use sounds from resources, load them like this
  if(FAILED(OpenWaveSound(hInstance, 1, IDR_WAVE1, "WAVE")))
  {
    MessageBox("Failed to load sound");
	return;
  }

  // Note: you do get the idea to test for errors when calling functions that return an HRESULT
  //  don't you? I'm going to cut the whole if(FAILED) thing out of the rest of the example to save
  //  some space, but just realize if a function returns and HRESULT value you can test for the
  //  function's success like I did above.

  // tweek some values of our loaded sounds
  PanSoundLeft(0);
  SetSoundVolume(0, 85);
  SetSoundFrequency(0, 10000);

  PanSound(1, 5000);
  SetSoundFrequency(1, 13000);

  // play our sounds with a little break in between
  PlaySound(0);

  Sleep(500);	// wait a half second

  PlaySound(1);

  // return sound 0 back to its standard properties
  PanSoundCenter(0);
  SetMaxVolume(0);
  SetSoundFreqStd(0);

  // and play it again, but only the first quarter second of it
  PlaySound(0);

  Sleep(250)	// wait a quarter second (i.e. 250 ms)

  StopSound(0);

  // now that we're done with everything, shut down DirectSound
  DSoundShutdown();

  // and with DirectSound is properly shutdown, its safe to kill the app
  return msg.wParam;
}

CONTACT INFO
If you have any questions, comments, or concerns, please do not hesitate to shoot me an e-mail at
drewdunsmore@hotmail.com.

Andrew S. Dunsmore