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_;
writeahead = writeahead_;
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
* won't fill it completely. */
@@ -123,6 +123,15 @@ void DSoundBuf::SetVolume(float vol)
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()
{
buf->Release();
@@ -132,22 +141,22 @@ bool DSoundBuf::get_output_buf(char **buffer, unsigned *bufsiz, int *play_pos, i
{
ASSERT(!buffer_locked);
DWORD cursor, junk, write;
DWORD cursorstart, junk, cursorend;
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 ) {
buf->Restore();
result = buf->GetCurrentPosition(&cursor, &write);
result = buf->GetCurrentPosition(&cursorstart, &cursorend);
}
if ( result != DS_OK ) {
LOG->Warn(hr_ssprintf(result, "DirectSound::GetCurrentPosition failed"));
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
* 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
* permanently increase our write-ahead. We want the smallest possible
* 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
* we seem to be consistently ahead, but that's getting a little messy ...
* of a robust way to do this.
*
* Also, writeahead should be a static (all buffers write ahead the same
* 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
* in GetLatencySeconds().
*/
#if 0
{
/* Figure out the amount of space we're not supposed to write to: */
int unwritable = write-cursor;
if(unwritable < 0) unwritable += buffersize; /* unwrap */
int first_byte_filled = write_cursor-buffer_bytes_filled;
if(first_byte_filled < 0) first_byte_filled += buffersize; /* unwrap */
if(writeahead < unwritable)
{
writeahead = unwritable*2;
LOG->Trace("boosted buffersize to %i", writeahead);
}
int current_cursor = cursorstart;
if(current_cursor < first_byte_filled) current_cursor += buffersize;
/* */
if(num_bytes_empty > buffersize - unwritable)
{
// writeahead += 512;
// LOG->Trace("underflow; bs now %i", writeahead);
}
/* The number of bytes that have been played since the last time we got here: */
int bytes_played = current_cursor - first_byte_filled;
buffer_bytes_filled -= bytes_played;
/* 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;
/* 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. */
if(num_bytes_empty < chunksize)
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. */
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. */
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. */
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;
if(write_cursor >= buffersize) write_cursor -= buffersize;
buffer_bytes_filled += num_bytes_empty;
*play_pos = last_cursor_pos;
/* Increment last_cursor_pos to point at where the data we're about to
@@ -266,7 +291,7 @@ void DSoundBuf::Stop()
{
buf->Stop();
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 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;
bool buffer_locked;