These tutorials focus mainly on OpenGL, Win32 programming and the ODE physics engine. OpenGL has moved on to great heights and I don't cover the newest features but cover all of the basic concepts you will need with working example programs.
Working with the Win32 API is a great way to get to the heart of Windows and is just as relevant today as ever before. Whereas ODE has been marginalized as hardware accelerated physics becomes more common.
Games and graphics utilities can be made quickly and easily using game engines like Unity so this and Linux development in general will be the focus of my next tutorials.
3D Sound Tutorial
By Alan Baylis 10/03/2002
Download the Demo with Source Code
To add sound to the program I have decided to use the BASS sound library written by Ian Luck. It has a great range of features and easy to use. I will only be focusing on how I implemented BASS in my program to add 3D sounds and will not be discussing any of the other numerous features. The best place to start learning about BASS is the documentation that comes with it here. From the documentation you should learn how to get BASS properly linked to your program (I have included the .lib file for people using Borland C++ Builder.) The following example is just one way to use BASS and not necessarily the best or correct way.
The main addition to this demo is two new classes called SOUND_SAMPLE and SOUND_CHANNEL in sound.h, both are compatible with my linked list and I have created a global list of each class type to handle the dynamic creation and deletion of them (but making them 'deletable' has not been written yet and should not be done.)
// Sound #include "bass.h" // Bass header int device; // Selected devicee BOOL lowqual; // Low quality option flag int SphereSector; // Current sector (leaf of bsp tree) of the sphere VECTOR SpherePosition; // Sphere position int numSamples = 0; // The number of sound samples in the list int numChannels = 0; // The number of channels in the list #include "sound.cpp" // Sound classes & functions LinkedList<SOUND_SAMPLE> SampleList; // The list of samples LinkedList<SOUND_CHANNEL> ChannelList; // The list of channels
After the inclusion of bass.h there is a device id to the selected output device followed by a flag for the low quality sound option. Then there is the id to the current sector (leaf number in the BSP tree) of the sphere's position and the position itself. After these are the number of samples and channels in each list, followed by the inclusion of the sound classes and functions, and then the two lists themselves.
class SOUND_SAMPLE { public: SOUND_SAMPLE(){}; ~SOUND_SAMPLE(){}; int Compare(const SOUND_SAMPLE& Sample); int GetMyPosition() const {return linkPosition;} void SetMyPosition(int newPosition) {linkPosition = newPosition;} int linkPosition; HSAMPLE hSample; // the sample's handle char name[32]; // filename int max; // number of simultaneous playbacks DWORD flags; // option flags DWORD volume; // sample volume float mindist; // distance at which it stops getting louder // distance at which it stops getting quieter/muted float maxdist; }; class SOUND_CHANNEL { public: SOUND_CHANNEL(){}; ~SOUND_CHANNEL(){}; int Compare(const SOUND_CHANNEL& Channel); int GetMyPosition() const {return linkPosition;} void SetMyPosition(int newPosition) {linkPosition = newPosition;} int linkPosition; HCHANNEL hChannel; // the channel's handle // handle of the sample associated with this channel HSAMPLE hSample; BASS_3DVECTOR position; // position BASS_3DVECTOR orientation; // orientation BASS_3DVECTOR velocity; // velocity int direction; // direction of the channel bool looped; // flag for looped sample bool intermittent; // flag for intermittent sound bool random; // flag for random intermittent sound unsigned int lowerrand; // minimum repeat time unsigned int upperrand; // maximum repeat time unsigned int interval; // time between plays // unique timer id (set this to the channel number) unsigned int idEvent; };
Above are the two classes and, if you have read the BASS documentation and used my linked list before, they should be clear to you. I will only discuss the few extra variables in SOUND_CHANNEL. They were necessary because I wanted four different ways to play sounds in my program:
On event - Play sound once on an keyboard press or other event.
Looped - Continuously playing sound that begins at a specified time.
Looped but intermittent - A sound that repeats after a specified interval.
Looped but randomly intermittent - A sound that repeats after a random interval.
The first type of sound doesn't use any of the extra variables in SOUND_CHANNEL and they may be left undefined. For the second type of sound the looped flag must be set to true and the intermittent flag set to false, the rest may be left undefined. The third type of sound is created by setting both looped and intermittent to true and the random flag to false and setting the interval to the time between plays (in milliseconds,) the rest may be undefined. For the fourth type of sound all three flags should be set to true, and the interval set to the time that the random sound should start, the lowerrand and upperrand variables should be set to the minimum and maximum times (in milliseconds) before repeating.
Back to the demo program, the first thing thing I do in my initialization routine is call a function called InitializeBASS() that checks the version info, initializes BASS, sets some 3D attributes and then starts the sound output. After this I call CreateSounds() which loads the samples, creates the channels and sets the timer if necessary. The important point is that for every sample there can be any number of channels using the same sample, each with different settings. So, to create a new sound you firstly add some constants to the enumerated constants at the top of sound.cpp (one constant for the sample and any number of constants for the channels) and then add a new section to the CreateSounds() routine, see the source code for an example. This is all that is needed to start any looped type of sound. If you want a sound to play on an event then all you need to do is load the sample and create the channel, when you want to play the sound just call PlayChannel with the enumerated channel name. If the position/orientation of a sound is changing then call UpdateChannel in the draw routine to update the channel each frame. Lastly, it is necessary to call UpdateListener in the draw routine to update the position/orientation/velocity of the player. When the program closes, call BASS_Stop() to stop the sound output and then BASS_Free() to free all resources.
I hope this helps to clarify how I add sounds to the program. This has been my first dabble with the BASS library, just enough to get the basics of sound working, and will work on an improved version later. If you have any questions or bug reports then send me an email.
References:
BASS Sound Library by Ian Luck
.