Thread: DirectSound - multiple sounds

  1. #1
    Confused Magos's Avatar
    Join Date
    Sep 2001
    Location
    Sweden
    Posts
    3,145

    DirectSound - multiple sounds

    With DirectSound, how do you play multiple sounds at the same time (of the same sound). Different sound buffers can play at the same time, but how can you start playing from the same buffer multiple times? It seems like a waste to create a new buffer with the same contents.
    MagosX.com

    Give a man a fish and you feed him for a day.
    Teach a man to fish and you feed him for a lifetime.

  2. #2
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    When you tell DirectSound to play a sample it will auto mix it into the main mix.

    To play the same sample multiple times, simply call the play function with the same sound in each call. If your sounds are setup as an array then you simply call playsound with sound[id].

    If you wish to manually mix them, which I do not recommend, simply take one sample from each sound to be played, add them together and divide by the number of sounds. Place this value in the final sound buffer and send this to DirectSound to be played.

    But this is a waste since it is already done for you in hardware.

  3. #3
    Confused Magos's Avatar
    Join Date
    Sep 2001
    Location
    Sweden
    Posts
    3,145
    Problem is, if I simply call Play again nothing happens until the sound has finished playing. Only then can I play it again.

    I can call the buffer's method SetCurrentPosition(0); which will play the sound from the beginning immediately, but unfortunately stop the last play.

    What I'm talking about is playing several sounds from the same buffer. I have no problems playing several sounds at the same time from several buffers in an array.
    MagosX.com

    Give a man a fish and you feed him for a day.
    Teach a man to fish and you feed him for a lifetime.

  4. #4
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    I'm not aware of DirectSound stopping playback if you pass the same sound to it that it is currently playing.

    Each new sound is its own object and therfore would have its own buffer so I'm not sure what you really want to do.
    Perhaps just a bit more explanation for me if you don't mind and I apologize in advance for my ignorance.

  5. #5
    Confused Magos's Avatar
    Join Date
    Sep 2001
    Location
    Sweden
    Posts
    3,145
    Ok, imagine a wav file: Sound1.wav (5 seconds length)

    At a point I wish to play this file. 1 second later I wish to play it again, while still letting the first one finish. This means they would overlap during 4 seconds.

    If I call Play again after 1 second, no new sound starts to play since the buffer is still playing the first.

    If I call SetCurrentPosition(0) followed by Play, the buffer starts over from the beginning (naturally).

    In both cases, only 1 instance of the sound can play at the same time. I want to be able to start a new sound whenever I want and make them overlap. NOT only having one play at a time.

    One solution would be to have another buffer with the exact same contents. But this is quite inefficient and not so good for an arbitrary amount of plays.

    Each new sound is its own object and therfore would have its own buffer
    That's the catch. It's the same sound I want to play multiple times, from 1 buffer.
    MagosX.com

    Give a man a fish and you feed him for a day.
    Teach a man to fish and you feed him for a lifetime.

  6. #6
    Confused Magos's Avatar
    Join Date
    Sep 2001
    Location
    Sweden
    Posts
    3,145
    I found a DuplicateSoundBuffer method that seemed to be exactly what I needed. It duplicates a buffer except for the data that is shared. However, I can't get it to work properly. It still only plays 1 sound at a time.

    Here is the play wav function:
    Code:
    BOOL Magos::MMusic::PlayWav(INT WavId, BOOL Looping, BOOL AllowMultipleSounds)
    {
    	//Data
    	LPDIRECTSOUNDBUFFER TempWav;
    
    	//Aborts if the wav ID is out of bounds
    	if((WavId < 0) || (WavId >= NrOfWavs))
    	{
    		return FALSE;
    	}
    
    	//Check if multiple sounds from the same buffer are allowed
    	if(AllowMultipleSounds)
    	{
    		//Creates a duplicate with shared memory
    		if(FAILED(DirectSound->DuplicateSoundBuffer(WavList[WavId], &TempWav)))
    		{
    			return FALSE;
    		}
    
    		//Adds the duplicate buffer to the play list
    		WavPlayList.push_back(TempWav);
    
    		//Play the wav
    		TempWav->SetCurrentPosition(0);
    		if(FAILED(TempWav->Play(0, 0, (Looping ? DSBPLAY_LOOPING : 0))))
    		{
    			return FALSE;
    		}
    
    		//Return success
    		return TRUE;
    	}
    
    	//Play the wav
    	WavList[WavId]->SetCurrentPosition(0);
    	if(FAILED(WavList[WavId]->Play(0, 0, (Looping ? DSBPLAY_LOOPING : 0))))
    	{
    		return FALSE;
    	}
    
    	//Return success
    	return TRUE;
    }
    Last edited by Magos; 03-02-2004 at 05:05 PM.
    MagosX.com

    Give a man a fish and you feed him for a day.
    Teach a man to fish and you feed him for a lifetime.

  7. #7
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Well I was sure that DirectSound could do this on the fly when you add another sound to the mix.

    If not then you need to create a function that will check for whether or not a sound is playing. If it is then you need to retrieve the samples from it that will overlap your new sample. Add these two samples together, divide by two, place that value in the buffer and send the new buffer data to DirectSound to be played. This is known as additive mixing and does suffer from some downfalls but it is easy to code and works reasonably well. Two sounds played at the same time really are just the two sounds averaged together at any one point in time.

    If you talk and I talk at the same time, the ending sound is our two voices averaged together.

    Example for 8-bit sound:

    Sound 1: ....255,128,154,233,143,201,110.......
    Sound 2: ....109,155,120,100,104,109,120.......
    Final: ....182,142,214,167,123,155,115......

    Send the Final: data to DirectSound to be played and you will hear both Sound 1 and Sound 2 at the same time from the same buffer.

    However, it is more complicated than that. If you know anything about how DOS sound was achieved then you will understand what is going on when a sound is played. When the sound card is setup the sound engine will program the DMA channel for the transfer to take place. Basically the DMA chip needs to know what DMA channel to use, what mode to use, what the sampling rate is, what the length of the buffer is. Now the sound card is programmed with the same info except that it is told what half of the buffer length is, not the total length. When the DSP chip signals that it has reached the length of the transfer, half of it because we programmed it that way, it will create a hardware interrupt on the pre-configured interrupt request - usually IRQ 5. Note that IRQ 5 is not actually the 5th interrupt in the system. Now you must write an interrupt handler to catch when this interrupt happens. When it does, you know you should either load the first or second half of the buffer and start playing the first or second half depending on where you are in the buffer.

    Sound buffers are circular buffers. Basically what happens is that when the first half is playing, the second is being loaded. When the second is being played, the first half is loaded. The reason for this is that the DMA chip will continually scan a portion of memory (in autoinit mode) increasing the BYTE or WORD pointer as it goes. If we programmed the sound card with the full transfer length, the DMA would get to the start of the buffer prior to us being able to load data there - resulting in a nasty click sound as the DMA transfers a string of 0's to the DSP.

    It is the same in DirectSound. Each sound buffer is split into two parts. Note that the actual sound data buffer or the buffer where all the sound data for one particular sound resides is not a circular buffer. The circular buffer is the small cache buffer, if you will, where chunks of the total sound are cached into, mixed with the other sound buffers or caches, and then the result of the mix is placed into a primary buffer and sent to the DSP via the DMA to be played. Confused??

    So you cannot just take sound data from any point in the buffers, mix it, and write out the result. It is quite possible that your data retrieval might cross two buffers. Because of this DirectSound will tell you where it's pointer is at in the primary buffer and secondar buffers. You must retrieve this value from DirectSound in order to correctly mix sound data in the buffers. Let's say we have a 256 byte buffer for all sound samples and for the primary sound. If DirectSound is at BYTE position 125 then you only should read in and mix 128-125 or 3 BYTES of sound data. If you do more than that you will waste cycles since when the pointer reaches the 128th position and interrupt will fire off and new data will be loaded between the 128 and 256 byte positions. Note that to gain access to the primary buffer you must tell DirectSound you wish to do this when you create the interface pointers. This will disable many other features as well and unless you are some kind of sound engine guru I would not advise doing this. The only people who need this kind of low-level access are those doing sound drivers like the Miles Sound Drivers or other sound engines that require more than DirectSound has to offer.
    For more information, consult the DX 9.0 SDK.

    Here is an example of the sound scheme:


    Sound Sample 1
    - RawSound Data
    - SoundBuffer
    Sound Sample 2
    - Raw Sound Data
    - SoundBuffer

    Let's assume a 256 byte buffer:
    • Load 256 bytes of Raw Sound Data into SoundBuffers for all SoundSamples wanting to be played. Increment sound sample data pointers accordingly.
    • Perform some type of mixing function on sound buffers - 128 bytes.
    • Place result of mixed data into primary buffer - 128 bytes The primary buffer is actually the portion of memory that the DMA chip was programmed to scan.
    • The DMA will send the data to the DSP w/o the CPU knowing about it at the desired sample rate.
    • Wash, rinse, repeat until sound samples are done - if we have reached the end of one of the sound sample, remove it from the playlist - no more data to cache and mix.



    Circular buffer:
    I - hardware interrupt is fired off here

    1. ----Being played---------I-------Being loaded---------
    2. ----Being loaded---------I-------Being played---------
    3. Goto 1
    Last edited by VirtualAce; 03-02-2004 at 05:16 PM.

  8. #8
    Confused Magos's Avatar
    Join Date
    Sep 2001
    Location
    Sweden
    Posts
    3,145
    Interesting, but I doubt I'll make a mixer myself. You have no experience in DuplicateSoundBuffer?

    Thanks for your help so far.
    MagosX.com

    Give a man a fish and you feed him for a day.
    Teach a man to fish and you feed him for a lifetime.

  9. #9
    Confused Magos's Avatar
    Join Date
    Sep 2001
    Location
    Sweden
    Posts
    3,145
    Ok, this is gonna sound so noobish it aint funny. The reason I failed to play multiple sounds from the same buffer at the same time was that I had a Music.Stop() in front of my Music.Play(). Gah, this is insane .

    Anyway, it works fine now.
    Last edited by Magos; 03-03-2004 at 11:26 AM.
    MagosX.com

    Give a man a fish and you feed him for a day.
    Teach a man to fish and you feed him for a lifetime.

  10. #10
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    LOL!!!!
    ROFL!!!



    Sorry, man, I've made similar mistakes so I'm laughing with you not at you.


    At least you got it working. You really had me confused cuz I thought the beauty of DirectSound is that it would already do all that jazz I talked about way up there in this thread.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 16
    Last Post: 06-08-2009, 03:03 PM
  2. Phantom redefinition
    By CodeMonkey in forum C++ Programming
    Replies: 6
    Last Post: 06-12-2005, 05:42 PM
  3. Linker errors - Multiple Source files
    By nkhambal in forum C Programming
    Replies: 3
    Last Post: 04-24-2005, 02:41 AM
  4. Multiple sounds in DX
    By VirtualAce in forum Game Programming
    Replies: 3
    Last Post: 10-13-2003, 10:59 PM
  5. Replies: 1
    Last Post: 05-01-2003, 02:52 PM