Advertisement

IDirectSoundBuffer duplication / multiple plays

Started by February 27, 2000 06:29 AM
7 comments, last by kieren_j 24 years, 6 months ago
When duplicating a DirectSound buffer with IDirectSound::DuplicateSoundBuffer , does the pointer to the destination need to be valid (i.e. a previously-created buffer)? I''d like to know, because I''m trying to create a "sound cluster" system; basically this is how I want it to work: (1) Load wave file "x.wav" into Sound structure THIS WORKS OK (2) Duplicate dsound buffer from Sound structure into 8 buffers (in an array) in a SoundCluster structure FAILS The problem is whenever I call the Duplicate function, it returns DSERR_INVALIDPARAM - for the DSBUFFERDESC param I pass the same one I used for the Sound structure, and for the DIRECTSOUNDBUFFER param I pass a buffer from the array. Why doesn''t this work? This is why I am trying creating the buffer first - is this the invalid parameter? But, this still returns DSERR_INVALIDPARAM; and so does CreateSoundBuffer()! Have some code:

void CSoundEngine::CreateSoundCluster(SoundCluster *sclst, Sound *snd)
{
	/*
	 * Creates a cluster of 8 sound buffers
	 * that can be played at will
	 */
	int i;
	

	/*
	 * Set details
	 */
	sclst->free_next = 0;
	sclst->dwSampleRate = snd->dwSampleRate;
	sclst->nBitsPerSample = snd->nBitsPerSample;
	sclst->nChannels = snd->nChannels;
	ZeroMemory(&sclst->wfex, sizeof(sclst->wfex));
	sclst->wfex.cbSize = 0;
	sclst->wfex.wFormatTag = WAVE_FORMAT_PCM;
	sclst->wfex.nAvgBytesPerSec = snd->wfex.nAvgBytesPerSec;
	sclst->wfex.nBlockAlign = snd->wfex.nBlockAlign;
	sclst->wfex.nChannels = snd->wfex.nChannels;
	sclst->wfex.nSamplesPerSec = snd->wfex.nSamplesPerSec;
	sclst->wfex.wBitsPerSample = snd->wfex.wBitsPerSample;
	sclst->dsbd.lpwfxFormat = &sclst->wfex;
	sclst->dsbd.dwSize = sizeof(sclst->dsbd);
	sclst->dsbd.dwReserved = 0;
	sclst->dsbd.dwFlags = snd->dsbd.dwFlags;
	sclst->dsbd.dwBufferBytes = snd->dsbd.dwBufferBytes;
		
	/*
	 * Create each sound individually using the
	 * passed reference sound structure
	 */
	for (i = 0; i < 8; i++)
	{
		/* 
		 * Create and copy to new sound buffer
		 */
		hRes = lpds->CreateSoundBuffer(&sclst->dsbd, &sclst->lpBuffer, NULL); /* FAILS */
	if (DS_OK != hRes)
		{
			lmEngine.DebugEvent("couldn''t create sound buffer");
		}
		hRes = lpds->DuplicateSoundBuffer(snd->lpBuffer, &sclst->lpBuffer); /* FAILS */
		if (DS_OK != hRes)
		{
			lmEngine.DebugEvent("couldn''t duplicate sound buffer");
		}
	}
}
 </pre>   </i>   
Well, I've created a relatively spiffy DSound wrapper using multiple buffers (mine actually implements a looped linked list to make copies of as many instances of the same sound the programmer desires, and works off the fact that the same sound will more or less always be the same duration... it of course assumes that these are short FX sounds, not things like music, I have a DMusic wrapper plugin for music that hooks into the DSound wrapper via compiler flag) and I tend to think of the duplicated buffers as not really buffers per se... they are more like file pointers. It would be silly to open 20 instances of a file, instead you open just one, and then add more pointers into the data you have open. Each of the duplicate buffers contains its own format info so you can do frequency shifting and panning and stuff per buffer, (Note: remember to reset your .wav format info for that buffer if you do shifting or other modifications and then want to use the buffer again later!) but they are all using the same physical memory space for the sound's actual data to be stored.
Here's my code that creates the first buffer in the ring, and then does as many duplicates in the looped linked list as the user asked for.

CDSoundWAVBuffer::CDSoundWAVBuffer( LPDIRECTSOUND lpSound, CString sFileName, DWORD dwCaps, int nCopies ){  if (nCopies < 1)    nCopies = 1;  // store the name (the filename)  m_sName = sFileName;  // store the number of copies of this the user wants  m_nCopies = nCopies;  // set up the caps  m_dwCaps = dwCaps;  // set up the loaded representation of this  CDSoundWAV *soundWAV = new CDSoundWAV(sFileName);  if (soundWAV->GetValidFlag() == FALSE)    return;  void* lpBuffer = NULL;  DSBUFFERDESC bufferDesc;  DWORD length;  memset(&bufferDesc, 0, sizeof(DSBUFFERDESC));  bufferDesc.dwBufferBytes = soundWAV->GetDataLength();  bufferDesc.dwFlags = DSBCAPS_CTRLPAN / DSBCAPS_CTRLVOLUME / DSBCAPS_CTRLFREQUENCY / DSBCAPS_STATIC / DSBCAPS_LOCSOFTWARE;  bufferDesc.dwSize = sizeof(DSBUFFERDESC);  bufferDesc.lpwfxFormat = soundWAV->GetWAVFormat();  // copy to the ring of buffers, first node  SOUNDWAVCOPY *copy = new SOUNDWAVCOPY;  SOUNDWAVCOPY *start = copy; // this will keep track of the list head for attachment at the end  SOUNDWAVCOPY *previous = copy;  copy->m_lpSoundBuffer = NULL;  LPDIRECTSOUNDBUFFER tempBuffer = NULL;  // create the buffer from the wrap that requested it  lpSound->CreateSoundBuffer(&bufferDesc, &tempBuffer, NULL);  copy->m_lpSoundBuffer = tempBuffer;  length = soundWAV->GetDataLength();  // copy the information into the new buffer  copy->m_lpSoundBuffer->Lock(0, length, &lpBuffer,                               &length, NULL, NULL, DSBLOCK_ENTIREBUFFER);  memcpy(lpBuffer, soundWAV->GetWAVData(), soundWAV->GetDataLength());  copy->m_lpSoundBuffer->Unlock(lpBuffer, soundWAV->GetDataLength(), NULL, 0);  copy->m_bufferNumber = 0;  // loop to self in case there is only 1 copy  copy->m_pNext = copy;   // set up the rest of the ring  if (nCopies > 1)  {    for (int i = 0; i < nCopies; i++)    {      copy = new SOUNDWAVCOPY;      lpSound->DuplicateSoundBuffer(start->m_lpSoundBuffer,©->m_lpSoundBuffer);      copy->m_bufferNumber = i + 1;      previous->m_pNext = copy;      previous = copy;    }  }    // complete the ring  copy->m_pNext = start;  // pointer to the ring  m_lpBufferRingHead = start;  // okay, tell it there's something here  m_bLoaded = TRUE;  // little cleanup  delete soundWAV;} // end CDSoundWAVBuffer::CDSoundWAVBuffer  


HTH
-fel

Edited by - felisandria on 2/27/00 1:25:29 PM
~ The opinions stated by this individual are the opinions of this individual and not the opinions of her company, any organization she might be part of, her parrot, or anyone else. ~
Advertisement
i am also currently implementing something like that (well, actually exactly the same)
but i have one problem i can''t quite figure out: how do i know when one of the duplicated buffers has stopped playing so i can free it''s memory again?
do i have to set a notify for that or should i check every frame if it is still playing (slow...) ?

thanks
ridcully
Hey sorry guys, I found the problem - stupid me, the sound was being loaded into Sound correctly (it said so) - BUT - after it tried to duplicate it! d''oh!
btw if anybody''s interested, the total code for my engine adds up to 110kb, after just one week! im happy anyway
Ridcully: why would you want to? I don''t free until the buffers all shut down... because it isn''t an actual image of the sound. Each duplicate is just a pointer to the sound''s image, so the size hit is only the format which isn''t that big... unless you mean "when done with that sound in general" like if you moved to a level that didn''t have, say, car horns anymore, in which case you could indicate that and release the entire buffer and copy set.

-fel
~ The opinions stated by this individual are the opinions of this individual and not the opinions of her company, any organization she might be part of, her parrot, or anyone else. ~
hmm, if you don''t clear your duplicated buffer once it has been played, wouldn''t you end up having thousands of space-waasting pointers to the same soundbuffer, assuming the same sound is played thousands of times in an applications life?
well, i don''t consider that very efficient
Advertisement
Oh. No. Absolutely not.

I have the programmer tell the wrapper the maximum number of times a sound can be simultaneously played... for instance, if they wanted a machine gun and had a single shot sound, and wanted 10 of them to be able to be played simultaneously, I would create a single sound buffer for it, and then create 9 duplicates, arranging them in a looped linked list. Again, to my knowledge, the big part (the part where the actual sound info is stored) is only in there once, the rest are just pointers into that information. When a copy of the buffer is done playing... it can be used to play the sound again! No use in deleting the buffer and making a new one, that''s way too slow, not to mention messy. Just keep using the same buffer over and over. The only time that a buffer can''t be reused is if it''s currently playing (in which case I wait for it to get done, because if that one''s playing there''s a high chance they are all playing) or it''s playing looping (in which case I skip over it and go to the next one and see if it''s busy, until I go all the way through the list).

-fel
~ The opinions stated by this individual are the opinions of this individual and not the opinions of her company, any organization she might be part of, her parrot, or anyone else. ~
hmm, that is the way i am doing it at the moment.
but i thought those duplicated buffers ARE wasting space, yet not much admittedly and i wanted to have a list containing unlimited duplicated buffers that are created upon request and deleted when they stop playing.
that was my ideay opposed to having a given array sizey this would be more flexible.

but if there is really no decent way of checking when a buffer stops playing and then deleting it, i think i''ll stick to this method you are using

thanks anyway
ridcully
You can ask a buffer if it''s currently playing, but polling (which you pretty much would have to use, I don''t know of any way to get it to fire an event message) is a major time hit, in addition to creation and deletion time hit. I suppose you could roughly estimate the amount of play time required for the buffer to completely play the sound and wait that long to poll, but that''s complicated.

-fel
~ The opinions stated by this individual are the opinions of this individual and not the opinions of her company, any organization she might be part of, her parrot, or anyone else. ~

This topic is closed to new replies.

Advertisement