change buffer tracking a bit to make underruns easier to detect
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user