Jump to content
  • Advertisement

Archived

This topic is now archived and is closed to further replies.

thorpe

Simple DSound streaming question

This topic is 6131 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I create my buffers 200 ms long and fill them with wave data and start playing them looping. When 100 ms has played, I fill up the just played 100 ms section with new wave data. The sounds however just loop and loop forever (with 200 ms interval). Does the buffer need to be the same size as the wave length or is my solution possible (and appropriate)? I''d really appreciate some replies, I''ve been trying to get this working for quite a while now. Johan Torp - http://www.destruction.nu

Share this post


Link to post
Share on other sites
Advertisement
Well, since you''re playing the buffer looped, I assume that''s why the sound just loops forever. My question would be is it looping the same sound over and over again? From your post, maybe your buffer size is too small. Have you tried using a 2 second buffer instead? This will give you a little more time to process the sound data. Anyhow, you need to supply a little more information on your problem. Are you using the notification events? Do you have some code to look at?

Share this post


Link to post
Share on other sites
when modify the buffer, you are positive that your locks() are actually working? check for error codes. also 200ms is a decent sized buffer, but something closer to 1-2seconds is proabally better if what you are playing does not have to be low latency (which i am assuming its not. you really should the buffer more then 2ways as well. 30-100ms chunks out of 1000-2000ms buffer would probally work better (make sure your chunks are all the same size, if they are not you will have to deal with the inconsistency)

Share this post


Link to post
Share on other sites
After many hours of checking my code I found some errors.

Now, long sounds play correctly, others seem to loop at a 1 s interval.

I have commented my code for you and I really hope you can find something wrong. Here goes nothing...


  
// SoundBuffer.cpp: implementation of the SoundBuffer class.

//

//////////////////////////////////////////////////////////////////////


#include "SoundBuffer.h"
#include "SoundEngine.h"
#include "SoundSource.h"
#include "Game.h"

#define DESIRED_BUFFER_LENGTH 500
#define DESIRED_PIECE_LENGTH 100
#define DESIRED_PIECES_PER_S (1000/(DESIRED_BUFFER_LENGTH/PIECES_PER_BUFFER))

//////////////////////////////////////////////////////////////////////

// Construction/Destruction

//////////////////////////////////////////////////////////////////////


SoundBuffer::SoundBuffer(Game * theGame, CWaveSoundRead* iTheWaveSoundRead,SoundSource * iTheSource, int Loop)
{
TheGame=theGame;
TheSource=iTheSource;
TheWave=iTheWaveSoundRead;
TheBuffer=NULL;
LOOPING=Loop;
BufferSize = 0;
PieceSize = 0;
PieceProgress = 0;
NextWriteOffset = 0;
Progress = 0;
LastPos = 0;
FoundEnd = FALSE;
CreateStreamingBuffer();

nextSoundBuffer=TheGame->TheEngine->TheBuffers;
if(nextSoundBuffer)
nextSoundBuffer->prevSoundBuffer=this;
TheGame->TheEngine->TheBuffers=this;
prevSoundBuffer=NULL;
}

SoundBuffer::~SoundBuffer()
{

TheWave=NULL;

if(nextSoundBuffer)
nextSoundBuffer->prevSoundBuffer=prevSoundBuffer;
if(prevSoundBuffer)
prevSoundBuffer->nextSoundBuffer=nextSoundBuffer;
if(TheGame->TheEngine->TheBuffers==this)
TheGame->TheEngine->TheBuffers=nextSoundBuffer;

if(TheBuffer){
TheSource->StopPlaying();
delete TheBuffer;
}

}


//-----------------------------------------------------------------------------

// Name: IsPlaying()

// Desc: Returns TRUE if playing, FALSE otherwise

//-----------------------------------------------------------------------------


BOOL SoundBuffer::IsPlaying(){
if(!TheBuffer)
return FALSE;
DWORD BufferStatus;
TheBuffer->GetStatus(&BufferStatus);
// We want to return a 1 or 0

if(BufferStatus&DSBSTATUS_PLAYING)
return TRUE;
return FALSE;

}
//-----------------------------------------------------------------------------

// Name: UpdateBuffer()

// Desc: This must be called called on regular intervals.

//-----------------------------------------------------------------------------

void SoundBuffer::UpdateBuffer(){
if(TheBuffer){
if(BufferPieces==1){
// We do no need to fill up more wave data since the buffer already

// contains all wave data.

if(!IsPlaying())
TheSource->StopPlaying();
return;
}
if(UpdateProgress()!=S_OK)
RestoreBuffers();
}
}
//-----------------------------------------------------------------------------

// Name: UpdateProgress()

// Desc: Checks if more wave data needs to be written and does that if needed.

//-----------------------------------------------------------------------------

HRESULT SoundBuffer::UpdateProgress()
{
HRESULT hr;
DWORD dwPlayPos;
DWORD dwWritePos;
DWORD dwPlayed;

if( FAILED( hr = TheBuffer->GetCurrentPosition( &dwPlayPos, &dwWritePos ) ) )
return hr;

if( dwPlayPos < LastPos ){
dwPlayed = BufferSize - LastPos + dwPlayPos;
}else
dwPlayed = dwPlayPos - LastPos;

Progress += dwPlayed;
LastPos = dwPlayPos;

// While we have played a whole piece, fill the space up

// with new wave data.

while(Progress>(PieceProgress+1-BufferPieces)*PieceSize &&
PieceProgress<Pieces){
// Write a piece

WriteMoreWaveData();
PieceProgress++;
}

if( Progress>=Pieces*PieceSize && !LOOPING ){
TheSource->StopPlaying();
}


return S_OK;
}




//-----------------------------------------------------------------------------

// Name: RestoreBuffers()

// Desc: Restore lost buffers and fill them up with sound if possible

//-----------------------------------------------------------------------------

HRESULT SoundBuffer::RestoreBuffers()
{
HRESULT hr;

if( NULL == TheBuffer )
return S_OK;

DWORD dwStatus;
if( FAILED( hr = TheBuffer->GetStatus( &dwStatus ) ) )
return hr;

if( dwStatus & DSBSTATUS_BUFFERLOST )
{
// Since the app could have just been activated, then

// DirectSound may not be giving us control yet, so

// the restoring the buffer may fail.

// If it does, sleep until DirectSound gives us control.

do
{
hr = TheBuffer->Restore();
if( hr == DSERR_BUFFERLOST )
Sleep( 10 );
}
while( hr = TheBuffer->Restore() );

if( FAILED( hr = FillBuffer( ) ) )
return hr;
}

return S_OK;
}


//-----------------------------------------------------------------------------

// Name: StopBuffer()

// Desc: Stop the DirectSound buffer

//-----------------------------------------------------------------------------

HRESULT SoundBuffer::StopBuffer()
{
if( NULL != TheBuffer ){
TheBuffer->Stop();
}

return S_OK;
}

//-----------------------------------------------------------------------------

// Name: WriteToBuffer()

// Desc: Writes wave data to the streaming DirectSound buffer

//-----------------------------------------------------------------------------

HRESULT SoundBuffer::WriteToBuffer( VOID* pbBuffer, DWORD dwBufferLength )
{
HRESULT hr;
UINT nActualBytesWritten;

if( !FoundEnd )
{
// Fill the DirectSound buffer with WAV data

if( FAILED( hr = TheWave->Read(dwBufferLength,
(BYTE*) pbBuffer,
&nActualBytesWritten ) ) )
return hr;
}
else
{
// Fill the DirectSound buffer with silence

FillMemory( pbBuffer, dwBufferLength,
(BYTE)( TheWave->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
nActualBytesWritten = dwBufferLength;
}

// If the number of bytes written is less than the

// amount we requested, we have a short file.

if( nActualBytesWritten < dwBufferLength )
{
if( !LOOPING )
{
FoundEnd = TRUE;

// Fill in silence for the rest of the buffer.

FillMemory( (BYTE*) pbBuffer + nActualBytesWritten,
dwBufferLength - nActualBytesWritten,
(BYTE)(TheWave->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
}
else
{
// We are looping.

UINT nWritten = nActualBytesWritten; // From previous call above.

while( nWritten < dwBufferLength )
{
// This will keep reading in until the buffer is full. For very short files.

if( FAILED( hr = TheWave->Reset() ) )
return hr;

if( FAILED( hr = TheWave->Read( (UINT)dwBufferLength - nWritten,
(BYTE*)pbBuffer + nWritten,
&nActualBytesWritten ) ) )
return hr;

nWritten += nActualBytesWritten;
}
}
}

return S_OK;
}




//-----------------------------------------------------------------------------

// Name: CreateStreamingBuffer()

// Desc: Creates a streaming buffer, and the notification events to handle

// filling it as sound is played

//-----------------------------------------------------------------------------

HRESULT SoundBuffer::CreateStreamingBuffer()
{
HRESULT hr;

TheGame->Beacon(18001);

// This engine works by dividing a streaming buffer into approximately pieces

// of DESIRED_PIECE_LENGTH unless the buffer is shorter than

// DESIRED_BUFFER_LENGTH ms. If that is the case then that sound is played

// without looping.

// Otherwise it starts by filling up the entire buffer with wave data. Once

// it has played a piece, it fills the played piece up with new wave data which

// will be played once the buffer has looped around.

// UpdateSound needs to be called at least once every DESIRED_PIECE_LENGTH ms.


// Smallest atomic piece of the buffer that is a sample (size/sample)

DWORD nBlockAlign = (DWORD)(TheWave->m_pwfx->nBlockAlign);
INT nSamplesPerSec = TheWave->m_pwfx->nSamplesPerSec;

// Get the total size

DWORD TotalSize = (TheWave->m_ckInRiff.cksize+nBlockAlign-1);

DWORD ms = (1000*TotalSize)/(nSamplesPerSec*nBlockAlign);
if(ms>DESIRED_BUFFER_LENGTH){
BufferPieces=DESIRED_BUFFER_LENGTH/DESIRED_PIECE_LENGTH;
// (Size per second / Piece per second)

PieceSize = (nSamplesPerSec * nBlockAlign) / BufferPieces;
}else{
// If buffer pieces == 1, we will ignore the piece process and play without looping

BufferPieces=1;
PieceSize=TotalSize;
}

// The PieceSize should be an integer multiple of nBlockAlign

PieceSize -= PieceSize % nBlockAlign;

// Calculate number of pieces to play

Pieces = (TotalSize+PieceSize-1)/PieceSize;

// The buffersize should approximately DESIRED_BUFFER_LENGTH ms of wav data

// unless the wave is shorter than that.

BufferSize = PieceSize * BufferPieces;
FoundEnd = FALSE;
Progress = 0;
LastPos = 0;

// Set up the direct sound buffer, and only request the flags needed

// since each requires some overhead and limits if the buffer can

// be hardware accelerated


DSBUFFERDESC dsbd;
ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
dsbd.dwSize = sizeof(DSBUFFERDESC);
dsbd.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2;
dsbd.dwBufferBytes = BufferSize;
dsbd.lpwfxFormat = TheWave->m_pwfx;

// Create a DirectSound buffer

if( FAILED( hr = TheGame->TheEngine->lpDS->CreateSoundBuffer( &dsbd, &TheBuffer, NULL ) ) )
return hr;
TheGame->Beacon(18002);
return S_OK;
}




//-----------------------------------------------------------------------------

// Name: PlayBuffer()

// Desc: Play the DirectSound buffer

//-----------------------------------------------------------------------------

HRESULT SoundBuffer::PlayBuffer()
{
TheGame->Beacon(17001);
HRESULT hr;
VOID* pbBuffer = NULL;

if( NULL == TheBuffer )
return E_FAIL;

// Restore the buffers if they are lost

if( FAILED( hr = RestoreBuffers() ) )
return hr;
TheGame->Beacon(17002);
// Fill the entire buffer with wave data

if( FAILED( hr = FillBuffer( ) ) )
return hr;
TheGame->Beacon(17003);
// Play with the LOOPING flag since the streaming buffer

// wraps around before the entire WAV is played unless

// there is only one buffer piece


DWORD Flags = DSBPLAY_LOOPING;
if(BufferPieces==1)
Flags=0;

if( FAILED( hr = TheBuffer->Play( 0, 0, Flags ) ) )
return hr;
TheGame->Beacon(17004);
return S_OK;
}




//-----------------------------------------------------------------------------

// Name: FillBuffer()

// Desc: Fills the DirectSound buffer completely up

//-----------------------------------------------------------------------------

HRESULT SoundBuffer::FillBuffer()
{
HRESULT hr;
VOID* pbBuffer = NULL;
DWORD dwBufferLength;

if( NULL == TheBuffer )
return E_FAIL;

FoundEnd = FALSE;
NextWriteOffset = 0; // Start writing from zero as soon as the first piece has played

Progress = 0;
PieceProgress=BufferPieces;
LastPos = 0;

// Reset the wav file to the beginning

TheWave->Reset();
TheBuffer->SetCurrentPosition( 0 );

// Lock the buffer down,

if( FAILED( hr = TheBuffer->Lock( 0, BufferSize,
&pbBuffer, &dwBufferLength,
NULL, NULL, 0L ) ) )
return hr;

// Fill the buffer with wav data

if( FAILED( hr = WriteToBuffer( pbBuffer, dwBufferLength ) ) )
return hr;

// Now unlock the buffer

TheBuffer->Unlock( pbBuffer, dwBufferLength, NULL, 0 );


return S_OK;
}



//-----------------------------------------------------------------------------

// Name: WriteMoreWaveData()

// Desc: Writes another piece of data to the buffer

//-----------------------------------------------------------------------------

HRESULT SoundBuffer::WriteMoreWaveData()
{
HRESULT hr;
VOID* pbBuffer = NULL;
DWORD dwBufferLength;

// Lock the buffer down

if( FAILED( hr = TheBuffer->Lock( NextWriteOffset, PieceSize,
&pbBuffer, &dwBufferLength, NULL, NULL, 0L ) ) )
return hr;

// Fill the buffer with wav data

if( FAILED( hr = WriteToBuffer( pbBuffer, dwBufferLength ) ) )
return hr;

// Now unlock the buffer

TheBuffer->Unlock( pbBuffer, dwBufferLength, NULL, 0 );
pbBuffer = NULL;

NextWriteOffset += PieceSize;
NextWriteOffset %= BufferSize; // Circular buffer


return S_OK;
}

void SoundBuffer::SetVolume(long Volume){
TheBuffer->SetVolume(Volume);
}
void SoundBuffer::SetPan(long Pan){
TheBuffer->SetPan(Pan);
}


Btw, am I supposed to use m_ckInRiff instead of m_ckIn? When the same sound plays twice, m_ckIn.ckSize is 0 for some reason so I used m_cKinRiff.

Thanks for your help, I''ve spent about a month trying to get my sound engine working and I''m so close now.

Johan Torp - http://www.destruction.nu

Share this post


Link to post
Share on other sites
Nevermind! I got it working! The error was in another object which adjusted pan and volume and restarted the sound as soon it quit playing.... thanks for your help!

Johan Torp - http://www.destruction.nu

Share this post


Link to post
Share on other sites
No, I didn''t get it working =(

When I call Stop(IDirectSoundBuffer) the buffer doesn''t actually stop for at least one certain sound (which is 2,4 s), It keeps looping in 1s intervals (at least I think) after it is supposed to have been stop. The stop() method however returns DS_OK and still the sound keeps on looping (I''m not sure whether it''s the beginning or the end of the sound since it sounds so alike).

Any clues???

Also I''mhaving problems playing several sounds at a time but I think that''s a different problem

Johan Torp - http://www.destruction.nu

Share this post


Link to post
Share on other sites
Well, I can''t say for sure what is wrong. You have a lot of stuff going on. I did notice that you''re polling the buffer to see if has finished playing one of the chunk, then filling that chunk with data. This method is fine, but can you be certain that the fill chunk routine is being called often enough? To determine the interval its being called, use timeGetTime() to get the CPU time (in milliseconds) then calculate the delta between calls. Sometimes, other parts of the code may be using too much time and not allowing this part of the code to fill the buffers with new data hence you get repetition. A better way to fill the buffer is to use the notification events in IDirectSoundBuffer8. Windows will interrupt your code and allow you to fill more data when the play cursor has reach the specified location. This method ensures that the fill data routine is being called. If you insist on using the polling method, you should make your buffers larger to give your code time to come back around and do the polling. If you have graphics and other things going on, 100ms may not be enough.

Share this post


Link to post
Share on other sites
Typically I have a buffer of about 500 ms and about that''s divided into 10 pieces. I start by filling in a pieces, and once the first piece has played, I fill it up with new data so I always have 450 ms to play with.

Could there be any problem with the fact that I use the same CWaveSoundRead for several buffers? Should I perhaps create a new one for each buffer?

You don''t have to bother reading the code too much, the error might as well be in another object, I just want to be sure that I got the principles right.

Johan Torp - http://www.destruction.nu

Share this post


Link to post
Share on other sites
After looking a bit on CWaveSoundREad, I realize that I should create several instances of them. However won''t that cause the same file being read into memory several times or does it stream data from the file?

Johan Torp - http://www.destruction.nu

Share this post


Link to post
Share on other sites
Each DirectSound buffer you create is basically a separate entity and they will all play at the same time. If this is your goal, then its ok. If you just want to play one audio file, then only one streaming buffer is needed. For multiple audio files, multiple buffers will be needed. Did you check to see if your refill function was doing its job often enough so that the play cursor doesn''t overtake the write cursor.

Share this post


Link to post
Share on other sites

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!