change buffer tracking a bit to make underruns easier to detect

This commit is contained in:
Glenn Maynard
2003-01-31 21:34:15 +00:00
parent 3f8b170325
commit 9e991a95cd
2 changed files with 57 additions and 31 deletions
+55 -30
View File
@@ -53,7 +53,7 @@ DSoundBuf::DSoundBuf(DSound &ds, DSoundBuf::hw hardware,
samplebits = samplebits_; samplebits = samplebits_;
writeahead = writeahead_; writeahead = writeahead_;
buffer_locked = false; buffer_locked = false;
last_cursor_pos = write_cursor = LastPosition = 0; last_cursor_pos = write_cursor = LastPosition = buffer_bytes_filled = 0;
/* The size of the actual DSound buffer. This can be large; we generally /* The size of the actual DSound buffer. This can be large; we generally
* won't fill it completely. */ * won't fill it completely. */
@@ -123,6 +123,15 @@ void DSoundBuf::SetVolume(float vol)
buf->SetVolume(max(int(1000 * vl2), DSBVOLUME_MIN)); buf->SetVolume(max(int(1000 * vl2), DSBVOLUME_MIN));
} }
/* Determine if "pos" is between "start" and "end", for a circular buffer. */
bool contained(int start, int end, int pos)
{
if(end >= start) /* start ... pos ... end */
return start <= pos && pos <= end;
else
return pos >= start || pos <= end;
}
DSoundBuf::~DSoundBuf() DSoundBuf::~DSoundBuf()
{ {
buf->Release(); buf->Release();
@@ -132,22 +141,22 @@ bool DSoundBuf::get_output_buf(char **buffer, unsigned *bufsiz, int *play_pos, i
{ {
ASSERT(!buffer_locked); ASSERT(!buffer_locked);
DWORD cursor, junk, write; DWORD cursorstart, junk, cursorend;
HRESULT result; HRESULT result;
result = buf->GetCurrentPosition(&cursor, &write); /* It's easiest to think of the cursor as a block, starting and ending at
* the two values returned by GetCurrentPosition, that we can't write to. */
result = buf->GetCurrentPosition(&cursorstart, &cursorend);
if ( result == DSERR_BUFFERLOST ) { if ( result == DSERR_BUFFERLOST ) {
buf->Restore(); buf->Restore();
result = buf->GetCurrentPosition(&cursor, &write); result = buf->GetCurrentPosition(&cursorstart, &cursorend);
} }
if ( result != DS_OK ) { if ( result != DS_OK ) {
LOG->Warn(hr_ssprintf(result, "DirectSound::GetCurrentPosition failed")); LOG->Warn(hr_ssprintf(result, "DirectSound::GetCurrentPosition failed"));
return false; return false;
} }
int num_bytes_empty = cursor - write_cursor;
if(num_bytes_empty <= 0) num_bytes_empty += buffersize; /* unwrap */
/* XXX We can figure out if we've underrun, and increase the write-ahead /* XXX We can figure out if we've underrun, and increase the write-ahead
* when it happens. Two problems: * when it happens. Two problems:
@@ -156,45 +165,59 @@ bool DSoundBuf::get_output_buf(char **buffer, unsigned *bufsiz, int *play_pos, i
* 2. We don't want a random underrun (eg. virus scanner starts) to * 2. We don't want a random underrun (eg. virus scanner starts) to
* permanently increase our write-ahead. We want the smallest possible * permanently increase our write-ahead. We want the smallest possible
* that will give us reliable audio in normal conditions. I'm not sure * that will give us reliable audio in normal conditions. I'm not sure
* of a robust way to do this. We could decrease the buffer size if * of a robust way to do this.
* we seem to be consistently ahead, but that's getting a little messy ...
* *
* Also, writeahead should be a static (all buffers write ahead the same * Also, writeahead should be a static (all buffers write ahead the same
* amount); writeahead in the ctor should be a hint only (initial value), * amount); writeahead in the ctor should be a hint only (initial value),
* and the sound driver should query a sound to get the current writeahead * and the sound driver should query a sound to get the current writeahead
* in GetLatencySeconds(). * in GetLatencySeconds().
*/ */
#if 0
{ {
/* Figure out the amount of space we're not supposed to write to: */ int first_byte_filled = write_cursor-buffer_bytes_filled;
int unwritable = write-cursor; if(first_byte_filled < 0) first_byte_filled += buffersize; /* unwrap */
if(unwritable < 0) unwritable += buffersize; /* unwrap */
if(writeahead < unwritable) int current_cursor = cursorstart;
{ if(current_cursor < first_byte_filled) current_cursor += buffersize;
writeahead = unwritable*2;
LOG->Trace("boosted buffersize to %i", writeahead);
}
/* */ /* The number of bytes that have been played since the last time we got here: */
if(num_bytes_empty > buffersize - unwritable) int bytes_played = current_cursor - first_byte_filled;
{ buffer_bytes_filled -= bytes_played;
// writeahead += 512;
// LOG->Trace("underflow; bs now %i", writeahead); /* Data between the play cursor and the write cursor is committed to be
} * played. If we don't actually have data there, we've underrun. */
if(!contained(first_byte_filled, write_cursor, cursorstart) ||
!contained(first_byte_filled, write_cursor, cursorend))
{
LOG->Trace("underrun: %i..%i filled but cursor at %i..%i (missed it by %i)",
first_byte_filled, write_cursor, cursorstart, cursorend, (cursorend - first_byte_filled + buffersize) % buffersize);
/* Pretend the space between the play and write cursor is filled
* with data, and continue filling from there. */
int no_write_zone_size = cursorend - cursorstart;
if(no_write_zone_size < 0) no_write_zone_size += buffersize; /* unwrap */
buffer_bytes_filled = no_write_zone_size;
write_cursor = cursorend;
/* Don't register another buffer underrun until the play cursor
* passes the new write cursor. */
}
} }
#endif
int num_bytes_filled = buffersize - num_bytes_empty;
if(num_bytes_filled > writeahead) /* If we already have enough bytes written ahead, stop. */
if(buffer_bytes_filled > writeahead)
return false; return false;
/* num_bytes_empty is now the actual amount of free buffer space. If it's int num_bytes_empty = buffersize-buffer_bytes_filled;
/* num_bytes_empty is the amount of free buffer space. If it's
* too small, come back later. */ * too small, come back later. */
if(num_bytes_empty < chunksize) if(num_bytes_empty < chunksize)
return false; return false;
/* I don't want to deal with DSound's split-circular-buffer locking stuff, so cap /* I don't want to deal with DSound's split-circular-buffer locking stuff, so clamp
* the writing space at the end of the physical buffer. */ * the writing space at the end of the physical buffer. */
num_bytes_empty = min(num_bytes_empty, buffersize - write_cursor); num_bytes_empty = min(num_bytes_empty, buffersize - write_cursor);
@@ -203,7 +226,7 @@ bool DSoundBuf::get_output_buf(char **buffer, unsigned *bufsiz, int *play_pos, i
* we'll give some data soon instead of lots of data later. */ * we'll give some data soon instead of lots of data later. */
num_bytes_empty = min(num_bytes_empty, chunksize); num_bytes_empty = min(num_bytes_empty, chunksize);
// LOG->Trace("gave %i at %i (%i, %i) %i filled", num_bytes_empty, write_cursor, cursor, write, num_bytes_filled ); // LOG->Trace("gave %i at %i (%i, %i) %i filled", num_bytes_empty, write_cursor, cursor, write, buffer_bytes_filled);
/* Lock the audio buffer. */ /* Lock the audio buffer. */
result = buf->Lock(write_cursor, num_bytes_empty, (LPVOID *)buffer, (DWORD *) bufsiz, NULL, &junk, 0); result = buf->Lock(write_cursor, num_bytes_empty, (LPVOID *)buffer, (DWORD *) bufsiz, NULL, &junk, 0);
@@ -219,6 +242,8 @@ bool DSoundBuf::get_output_buf(char **buffer, unsigned *bufsiz, int *play_pos, i
write_cursor += num_bytes_empty; write_cursor += num_bytes_empty;
if(write_cursor >= buffersize) write_cursor -= buffersize; if(write_cursor >= buffersize) write_cursor -= buffersize;
buffer_bytes_filled += num_bytes_empty;
*play_pos = last_cursor_pos; *play_pos = last_cursor_pos;
/* Increment last_cursor_pos to point at where the data we're about to /* Increment last_cursor_pos to point at where the data we're about to
@@ -266,7 +291,7 @@ void DSoundBuf::Stop()
{ {
buf->Stop(); buf->Stop();
buf->SetCurrentPosition(0); buf->SetCurrentPosition(0);
last_cursor_pos = LastPosition = write_cursor = 0; last_cursor_pos = LastPosition = write_cursor = buffer_bytes_filled = 0;
} }
+2 -1
View File
@@ -29,7 +29,8 @@ class DSoundBuf
int buffersize_frames() const { return buffersize / samplesize(); } int buffersize_frames() const { return buffersize / samplesize(); }
int samplesize() const { return channels*samplebits/8; } int samplesize() const { return channels*samplebits/8; }
int write_cursor, last_cursor_pos; /* frames */ int write_cursor, buffer_bytes_filled; /* bytes */
int last_cursor_pos; /* frames */
mutable int LastPosition; mutable int LastPosition;
bool buffer_locked; bool buffer_locked;