Thread: Low latency sound effects

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

    Low latency sound effects

    For those who may be working on a sound engine or those who have a copy of mine, I've added some code to reduce the latency down to some pretty insane rates. This idea was inspired by the book DirectX9 Audio Exposed which explained how to do it correctly. Thanks a million.

    Code:
    void CDXAudio::SetLatency(DWORD dwLatencyMs)
    {
    IDirectMusicAudioPath *pPath;
    if (SUCCEEDED(Performance->GetDefaultAudioPath(&pPath)))
    {
    	IKsControl *pControl;
    	pPath->GetObjectInPath(0,
    						 DMUS_PATH_PORT,
    						 0,
    						 GUID_All_Objects,
    						 0,
    						 IID_IKsControl,
    						 (void **)&pControl);
    	if (pControl)
    	{
    	 KSPROPERTY ksp;
    	 DWORD dwData;
    	 ULONG cb;
    	 dwData=dwLatencyMs;
    	 ksp.Set=GUID_DMUS_PROP_WritePeriod;
    	 ksp.Id=0;
    	 ksp.Flags=KSPROPERTY_TYPE_SET;
    	 pControl->KsProperty(&ksp,sizeof(ksp),&dwData,sizeof(dwData),&cb);
    	 dwData=dwLatencyMs;
    	 ksp.Set=GUID_DMUS_PROP_WriteLatency;
    	 ksp.Id=0;
    	 ksp.Flags=KSPROPERTY_TYPE_SET;
    	 pControl->KsProperty(&ksp,sizeof(ksp),&dwData,sizeof(dwData),&cb);
    	 pControl->Release();
    	}
    	pPath->Release();
    }
    }
    This only works on DirectX 9.0 and it is possible that with some older sound card drivers this will certainly glitch the sound. But on newer cards this will cause hair-trigger sound events to work perfectly and they won't hesitate, glitch, or have delays before they begin playing. Very nice.

    Attached is the source code and header for the sound engine thus far. It is a work in progress and still does not yet support multiple audio paths. I'm working on a readme file that will explain how to use the code.

    Perhaps Shakti can shed some light on how to use it.

    Since this sound engine works off of ID numbers my suggestion is to place your sound ID's in a global struct:

    Code:
    struct tagGameSounds
    {
    unsigned int PlayerFire1;
    unsigned int Explosion1;
    }GameSounds;
     
    SFXSys.Play(GameSounds.PlayerFire1);
    SFXSys.Play(Explosion1);
    There is no need to make the ID's local to the class using the sound. This only complicates the code and also induces some performance hits for retrieving the ID from the class.

    Here is a sample of the setup code for asteroids. This is all that needs to be done for setting up the sound system, as of version 1.0.

    Code:
    bool Setup(HINSTANCE hInstance,HWND hWindow)
    {
    SFX.Create(1);
    Music.Create(1);
    SFX.SetLatency(10);
     
    MusicSys=new CDXSoundEmitter();
    SFXSys=new CDXSoundEmitter();
     
    SFXSys->Create(SFX.GetLoader(),SFX.GetPerformance());
    MusicSys->Create(Music.GetLoader(),Music.GetPerformance());
    Sounds.PlayerFire=(SFXSys->LoadSound(L"Sounds/PlayerFire1.wav"));
    Sounds.PlayerFire2=(SFXSys->LoadSound(L"Sounds/PlayerFire2.wav"));
    Sounds.PlayerRocket=(SFXSys->LoadSound(L"Sounds/PlayerRocket.wav"));
    SFXSys->SetLooping(Sounds.PlayerRocket,true);
    ...
    First place a reference to the sound system class either globally or in a class. I chose globally so that everyone can easily play sounds without having to mess with C++ class membership rules.

    Instantiate CDXAudio
    All constructors for these classes take no parameters so they can be instantiated like this:

    CDXAudio SFX;

    Call Create to actually init the audio system
    Now you must call Create() and pass the desired number of pchannels you wish to use to the system. Note this is for the ENTIRE sound system and it will never use more than what you specify.

    SFX.Create(10);

    This creates a sound system that uses 10 pchannels. This is only important for musical compositions. If you are playing wav files all you need is 1 pchannel.

    Next you will want set the latency for the system.

    Set the latency
    SFX.SetLatency(10);

    This sets the latency to 10ms. You can go as low as you want here, but I don't recommend anything below 5.

    Note that this should only be performed on newer sound cards and under DirectX 9.0. Otherwise you will have serious issues. You might want to include this as a game option in your front end system so the user can decided if they want the high latency or normal latency sound effects. You might think no big deal but here is the diff:

    Normal latency - 55 to 85 ms
    Reduced latency - 5 to 10 ms

    As you can see there is a huge difference between the latencies. This will translate into major gains as you develop your hair-trigger sound effects like guns firing, explosions, etc.

    Create a CDXSoundEmitter
    This might seem confusing but you do not play sounds through the Audio system itself. You must create a sound emitter object and then load sounds into the object and play them using the object. I chose this design because later when I implement Audio paths it will be a lot easier to create very cool sound effects that are object-based and not engine-based.

    So instantiate your CDXSoundEmitter.

    CDXSoundEmitter SFXSys=new CDXSoundEmitter;

    Now you must create the actual sound emitter. Just because the object exists does not mean it is ready to use. You must call Create() and pass in the Loader object and the Performance object.

    SFXSys->Create(SFX.GetLoader(),SFX.GetPerformance());

    Load the sounds
    Loading the sounds is extremely easy. Just call Load and it will return the sound ID for that sound. Save the ID because all future references to the sound will not be file based or name based, they will use the ID returned from LoadSound().

    unsigned int mysound=SFXSys->LoadSound(L<path_to_sound>);

    The L is used because these are WCHARs not just CHARs. Im working on a function that will also take standard strings but I'm not done with it yet. So use L"mysound.wav" until then.

    Play the sound
    Now to play the sound all you do is this:

    1. Pick the correct sound emitter.
    In my example we only have 1 sound emitter which is what we are using game-wide so there are not multiple sound emitters. In a 3D game there will be multiple sound emitters combined with engine-wide-sounds as well.

    2. Call Play and pass in the ID of the sound to play.
    SFXSys->Play(mysound);

    Done.

    You can specify to Play() the type of segment this is by specifying CDX_PRIMARY_SEG or CDX_SECONDARY_SEG in the second parameter. In defaults to the latter because using CDX_PRIMARY_SEG will stop all other sounds from playing when you play the current one. Only one primary segment can play at any one time.

    You can also use PlayWithCheck() which will only play the sound if the sound is NOT currently playing. This is useful for certain situations in which you do not want multiple layered instances of the sound playing.

    To stop the sound just call Stop() and pass the ID of the sound to stop. It is not an error to call Stop() on a sound that is not playing. The call will simply be ignored.

    There are some other features but you can look at the code and figure those out.

    NOTE: Specific instantiation of CDXSoundSegment is an incorrect use of this system. CDXSoundSegment is not a public class to be used by the programmer. This hides the actual tedium of the Sound segment from the programmer and has been designed this way to make this system very easy to use.

    Future revisions will fix/add:
    • Early truncation of sounds bug (using PMSGS)
    • Pitch shifting, reverb, and other user DMOs
    • Occlusional sound effects
    • Many, many more functions to be used for music systems
    • Might use this sound system as a true COM object with interfaces.
    • 3D positional audio through multiple sound emitters using multiple DMOs and effects.
    • Scripting support using VBScript in DirectMusic
    • Notification support using PMSGS - lyric tracks, etc.
    Last edited by VirtualAce; 12-21-2004 at 03:12 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. DirectX Question
    By bladerunner627 in forum Game Programming
    Replies: 2
    Last Post: 04-04-2005, 11:55 AM
  2. Generating noise-like sound effects
    By DougDbug in forum Game Programming
    Replies: 1
    Last Post: 02-12-2005, 02:57 PM
  3. Updated sound engine code
    By VirtualAce in forum Game Programming
    Replies: 8
    Last Post: 11-18-2004, 12:38 PM
  4. DirectSound - multiple sounds
    By Magos in forum Game Programming
    Replies: 9
    Last Post: 03-03-2004, 04:33 PM
  5. sounds?
    By BODYBUILDNERD in forum C++ Programming
    Replies: 6
    Last Post: 12-06-2002, 03:34 PM