* Cache the silence buffer instead of allocating a new one each time and

writing zeros into it in a real time thread.
* Use a different method to calculate the drift. The old method would generate
  a stable drift value after some time, but somehow the drift gave problems
  after a while. For example on Haiku, the MediaPlayer would stop playing
  stuff after several hours of uptime. The new method calculates the drift
  for just the last written buffer, like the Multi-Audio node does, but
  averages the result over the last 64 drift values in order to smooth out
  sudden changes. Seems to work well on Haiku, on BeOS, the sound quality seems
  to be a bit worse (the BeOS mixer actually resamples each buffer according
  to current drift).


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26125 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus
2008-06-24 21:06:45 +00:00
parent 285b754fa7
commit 5ff7609dce
2 changed files with 50 additions and 59 deletions
@@ -104,7 +104,7 @@ status_t OpenSoundDeviceEngine::Open(int mode)
// HD Audio hardware, it is too short. However, I seem to remember the
// HD Audio supports 32 bit sample width (while C-Media supports "only"
// 16). If OSS uses the same 2048 bytes even for 32 bit/sample, then I
// could see how that would be asking too much, since that would
// could see how that would be asking for too much, since that would
// effectively half the latency.
#ifdef HAIKU_TARGET_PLATFORM_HAIKU
v = 5;
@@ -100,45 +100,20 @@ public:
RecycleAllBuffers();
}
status_t FillBuffer(BBuffer* buffer)
status_t Write(void* data, size_t size)
{
CALLED();
ssize_t written = fRealEngine->Write(
buffer->Data(), buffer->SizeUsed());
ssize_t written = fRealEngine->Write(data, size);
if (written < 0)
return (status_t)written;
if (written < (ssize_t)buffer->SizeUsed())
if (written < (ssize_t)size)
return B_IO_ERROR;
return B_OK;
}
void WriteSilence(size_t bytes)
{
CALLED();
// TODO: A silenceBuffer could be cached!
uint8 formatSilence = 0;
if (fInput.format.u.raw_audio.format
== media_raw_audio_format::B_AUDIO_UCHAR)
formatSilence = 128;
size_t bufferSize = 2048;
char buffer[bufferSize];
memset(buffer, formatSilence, bufferSize);
while (bytes) {
size_t chunk = MIN(bytes, bufferSize);
ssize_t written = fRealEngine->Write(buffer, chunk);
if (written < 0)
return;
bytes -= written;
}
}
void WriteTestTone(size_t bytes)
{
// phase of the sine wave
@@ -1630,7 +1605,9 @@ OpenSoundNode::TimeSourceOp(const time_source_op_info& op, void* _reserved)
}
break;
case B_TIMESOURCE_SEEK:
TRACE("TimeSourceOp op B_TIMESOURCE_SEEK\n");
// TRACE("TimeSourceOp op B_TIMESOURCE_SEEK\n");
printf("TimeSourceOp op B_TIMESOURCE_SEEK, real %lld, "
"perf %lld\n", op.real_time, op.performance_time);
BroadcastTimeWarp(op.real_time, op.performance_time);
break;
default:
@@ -2245,13 +2222,29 @@ OpenSoundNode::_PlayThread(NodeInput* input)
"size: %ld", driverBufferSize, bufferSize);
}
// cache a silence buffer
uint8 silenceBuffer[bufferSize];
uint8 formatSilence = 0;
if (input->fInput.format.u.raw_audio.format
== media_raw_audio_format::B_AUDIO_UCHAR)
formatSilence = 128;
memset(silenceBuffer, formatSilence, bufferSize);
// start by writing the OSS driver buffer size of silence
// so that the first call to write() already blocks for (almost) the
// buffer duration
input->WriteSilence(driverBufferSize);
input->Write(silenceBuffer, bufferSize);
int64 bytesWritten = 0;
bigtime_t realTimeStart = RealTime();
bigtime_t lastRealTime = RealTime();
bigtime_t lastPerformanceTime = 0;
const int32 driftValueCount = 64;
int32 currentDriftValueIndex = 0;
float driftValues[driftValueCount];
for (int32 i = 0; i < driftValueCount; i++)
driftValues[i] = 1.0;
do {
if (!fDevice->Locker()->Lock())
@@ -2275,47 +2268,45 @@ OpenSoundNode::_PlayThread(NodeInput* input)
// buffer->Recycle();
//continue;
int32 additionalBytesWritten = 0;
if (buffer != NULL) {
input->FillBuffer(buffer);
bytesWritten += buffer->SizeUsed();
if (input->Write(buffer->Data(), buffer->SizeUsed()) == B_OK)
additionalBytesWritten = buffer->SizeUsed();
buffer->Recycle();
} else {
if (input->fInput.source != media_source::null) {
//printf("no buffer - silence\n");
input->WriteSilence(bufferSize);
bytesWritten += bufferSize;
} else {
//printf("no buffer - snooze\n");
snooze(3000);
// NOTE - stippi: I think this is what needs to happen
// anyways, or the "drift" will be totally screwed:
realTimeStart = RealTime();
bytesWritten = 0;
}
input->Write(silenceBuffer, bufferSize);
additionalBytesWritten = bufferSize;
}
// TODO: do not assume channel 0 will always be running!
// update the timesource
if (input->fEngineIndex == 0 && input->fThread >= 0) {
bigtime_t now = RealTime();
// NOTE stippi: I am unsure which realtime start time to use here,
// "fTimeSourceStartTime" is the time at which the Timesource start op
// was received, realTimeStart should be more precise though for calculating
// the "drift":
bigtime_t realPlaybackDuration = now - realTimeStart;
//bigtime_t realPlaybackDuration = now - fTimeSourceStartTime;
bigtime_t estimatedPlaybackDuration
bigtime_t realTime = RealTime();
bigtime_t realPlaybackDuration = realTime - lastRealTime;
bigtime_t performanceTime
= time_for_buffer(bytesWritten, input->fInput.format);
float drift = (double)estimatedPlaybackDuration
/ realPlaybackDuration;
float drift = (double)(performanceTime
- lastPerformanceTime) / realPlaybackDuration;
lastPerformanceTime = performanceTime;
lastRealTime = realTime;
driftValues[currentDriftValueIndex++] = drift;
if (currentDriftValueIndex == driftValueCount)
currentDriftValueIndex = 0;
drift = 0.0;
for (int32 i = 0; i < driftValueCount; i++)
drift += driftValues[i];
drift /= driftValueCount;
if (fDevice->Locker()->Lock()) {
if (input->fThread >= 0)
_UpdateTimeSource(estimatedPlaybackDuration, now, drift);
_UpdateTimeSource(performanceTime, realTime, drift);
fDevice->Locker()->Unlock();
}
}
bytesWritten += additionalBytesWritten;
} while (input->fThread > -1);
@@ -2507,8 +2498,8 @@ OpenSoundNode::_UpdateTimeSource(bigtime_t performanceTime,
PublishTime(performanceTime, realTime, drift);
// TRACE("_UpdateTimeSource() perfTime : %lli, realTime : %lli,
// drift : %f\n", perfTime, realTime, drift);
// TRACE("_UpdateTimeSource() perfTime : %lli, realTime : %lli, "
// "drift : %f\n", perfTime, realTime, drift);
}