Free Portals Example
Click here to go to the Portals example
http://au.ebid.net/perl/normal.cgi?ref=855501&mo=register-main

3D Sound Tutorial


By Alan Baylis 10/03/2002



Download the Demo with Source

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


Copyright © 1998 - 2010 Alan Baylis, All Rights Reserved