Thread: Updated sound engine code

Threaded View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607

    Updated sound engine code

    To all who have been waiting for me to develop my sound engine, I am sorry for the wait but it is getting much closer to being completed.

    The main problem I was having was the sound effect(s) were not mixing and thus only one sound could be played at one time. I have fixed this and it was pure oversight on my part that caused the problem.

    Shakti, you probably want to read this very closely because the code you have thus far only needs to be altered just a little bit to play multiple sounds. Since you are not using 3D audiopaths, all you really need to do is setup a sound emitter class with a vector of sounds relating to that sound emitter. Here is how to fix the code:

    In the call to IDirectMusicPerformance8::PlaySegmentEx() you must specify the segment as being a SECONDARY segment. I was specifying NULL which defaulted to a PRIMARY segment. There can only be one primary segment playing at any one time. If another primary segment needs to play the currently playing one is stopped and the new one is started. This is rather ugly. Primary segments are really intended for music use only as they do not have much use in sound effects. When you declare the segment as a secondary segment each instance of the playing sound will be auto-mixed into the DirectSoundBuffer8 relating to that sound and then mixed down into the primary buffer to be played. This is what you want to do.

    Here are updated versions of CDXAudio, CDXSoundSegment, and CDXSoundEmitter classes.

    Usage instructions

    Instantiation/Creation of CDXAudio
    First you must instantiate the CDXAudio class. The constructor for this class does not take any parameters and can simply be instantiated globally like this:

    Code:
    #include "CDXAudio.h"
     
    CDXAudio SoundEngine;
    Initialization of CDXAudio
    Second, you must call CDXAudio::Create(). This will auto-setup the IDirectMusicPerformance8 and IDirectMusicLoader8 interfaces. This create function will also do all of the necessary COM intialization setup so that you do not have to mess with it.

    This is where it gets a little fuzzy. The way that DirectMusic works is the Loader loads the sample into the Segment and then the Segment downloads the data into the Performance. Then the Performance can play/stop and do other things with the sound. This makes for a very strange class setup. Here is what I opted to do.

    Object-based sound engine
    My sound engine is object-based. In other words every sound played comes from some type of object in the world. So if you have a tank in your game that has engine sounds, bullets sounds, and firing sounds it needs to load all these sounds at initialization. This is quite simple really. Each object is a CDXSoundEmitter object. The CDXSoundEmitter object holds pointers to the IDirectMusicPerformance8 and IDirectMusicLoader8 interfaces and they are declared as public for quick access to them. I saw no benefit in creating accessor functions just to return a pointer and for the sake of simply hiding data. Data hiding just adds unnecessary stack frame overhead in this case. If you want to implement ambient sound effects simply create a CDXSoundEmitter and load all the ambient sounds into it. You may wish to derive from it in order to add your own functionality such as to add a randomness to when/how ambient sounds are played.

    Initialization of CDXSoundEmitter
    Ok so you have your CDXSoundEmitter. The class constructor does not take any parameters.

    To init the class you MUST pass the valid IDirectMusicPerformance8 and IDirectMusicLoader8 interface pointers to the CDXSoundEmitter::Create() function. These were created when you called CDXAudio::Create(). To retrieve the pointers from CDXAudio use the following functions.

    CDXAudio::GetPerformance()
    CDXAudio::GetLoader()

    Code example
    Code:
     
    #include "CDXAudio.h"
     
    CDXAudio SoundEngine;
    CDXSoundEmitter TestEmitter;
     
    void Setup(void)
    {
    //Create the audio object and init
    SoundEngine.Create();
     
    TestEmitter.Create(SoundEngine.GetLoader(),SoundEngine.GetPerformance());
     
    }
    Loading sounds into the CDXSoundEmitter class
    Ok now you have a valid CDXAudio object and a valid CDXSoundEmitter object. All that is left to do is load the sounds into the object.

    This is done by calling CDXSoundEmitter::LoadSound(WCHAR *_Filename)

    This next part is very important. Since any one object can emit more than one sound simply keeping track of one Segment to play is not sufficient. CDXSoundEmitter holds a vector of CDXSegment objects. Note that you do not and should not directly instantiate CDXSegment, this is already done for you. Here is what happens inside of CDXSoundEmitter::LoadSound()
    • A temporary CDXSegment object is created.
    • The file provided is opened and the data is loaded into the IDirectMusicSegment8 pointer (CDXSegment::Segment).
    • The object is then added to the CDXSegment vector inside of CDXSoundEmitter.
    • The temporary object is deleted and the size of the vector is returned to the caller. This value is the sound ID number and is extremely important. All future calls to play, stop, change volume, pan, frequency, etc., for this sound segment will be accessed via this ID number.

    NOTE: All access to the sounds for your sound emitter are provided through the CDXSoundEmitter class interface. Directly accessing CDXSegment class members should be avoided. In future releases I will ensure that CDXSegment cannot be misused in this way.


    What this means for the programmer is that he/she does not have to mess with sound segments at all. All that needs to be done to operate on a certain sound is to save the ID number returned from CDXSoundEmitter::LoadSound() and then pass that ID number to the desired CDXSoundEmitter function. For instance here is code that will play a sound.

    Code:
    unsigned int test_sound=TestEmitter.LoadSound(L"test.wav");
    TestEmitter.Play(test_sound);
    Checking to see if the sound is currently stopped/playing
    To test to see if the sound in question is already playing simply do this:

    Code:
    ...
    if (TestEmitter.IsPlaying(sound_ID)) 
    {
    //Sound is currently playing
    }
    ...
    I have not provided a function to see if the sound has stopped because this can be deduced from IsPlaying().

    Setting the volume for the sound
    To set the volume for the sound (only prototyped in the class - not functional yet):

    Code:
     
    ...
    TestEmitter.SetVolume(sound_ID,.5f);
    ...
    The second parameter here might be a bit confusing for you. Instead of messing with decibels and strange values I have opted to use normalized volume values. A value of 0.0f means the sound is turned all the way down or off. A sound value of 1.0f means the sound is at max volume. This allows the programmer to easily implement volume linear interpolation effects.


    For a complete description of all the classes and functions please consult CDXAudio.h


    AudioPaths and 3D AudioPaths
    This sound system does not implement 3D audiopaths or audiopaths as of yet and as such no spatially oriented sounds are supported. It is in my code base but has been disabled. It really isn't that much more code and simply means creating a class to encapsulate DirectMusic audio paths and then including a pointer to that class inside of CDXSoundEmitter. I'm tweaking that system right now. Also scripting is coded but not currently supported, but will be available soon. This will really aid in creating cool sound effects.
    I also hope to include occlusion sound effects and other effects that can easily be done inside of DirectMusic.

    Problems/Bug reports
    Should you have any trouble using this module and/or have found a bug I've overlooked or would like to add some functionality, please post in this thread.

    SFX Latency
    I'm also working on some code to interface with the driver to dial down the latency for sound effects to insane levels. This is only supported under DirectX9 and older sound cards might play some sound glitches if you attempt to use this on them. Music as of yet is also not completed. In my engine the music and sound effects are SEPARATE Performances. This will eliminate pchannel, groove level, chord progression, and tempo change interference between music and sfx. Thanks to Scott Selfon and Todor J. Fay for writing DirectX9 Audio Exposed. Much of my sound code and sound system structure was influenced by their suggestions.

    Included in the ZIP
    Here are the files in the zip:

    CDXAudio.h - header for CDXAudio.cpp
    CDXAudio.cpp - sound engine module
    CQuickCom.h - macros for working with COM
    Last edited by VirtualAce; 11-15-2004 at 01:19 AM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. how do the game engine and the api interact?
    By Shadow12345 in forum Game Programming
    Replies: 9
    Last Post: 12-08-2010, 12:08 AM
  2. Enforcing Machine Code Restrictions?
    By SMurf in forum Tech Board
    Replies: 21
    Last Post: 03-30-2009, 07:34 AM
  3. Low latency sound effects
    By VirtualAce in forum Game Programming
    Replies: 0
    Last Post: 12-21-2004, 01:58 AM
  4. Example code for threads and sound
    By CSoFun in forum C++ Programming
    Replies: 2
    Last Post: 01-21-2003, 09:49 PM
  5. Interface Question
    By smog890 in forum C Programming
    Replies: 11
    Last Post: 06-03-2002, 05:06 PM