static const char* kHardwareLockedString = "Hardware lock is taken\n";
//static const nsecs_t kStandbyTimeInNsecs = seconds(3);
-static const unsigned long kBufferRecoveryInUsecs = 2000;
-static const unsigned long kMaxBufferRecoveryInUsecs = 20000;
static const float MAX_GAIN = 4096.0f;
// retry counts for buffer fill timeout
// in both cases "unstop" the track
if (track->isPaused()) {
track->mState = TrackBase::RESUMING;
- LOGV("PAUSED => RESUMING (%d)", track->name());
+ LOGV("PAUSED => RESUMING (%d) on thread %p", track->name(), this);
} else {
track->mState = TrackBase::ACTIVE;
- LOGV("? => ACTIVE (%d)", track->name());
+ LOGV("? => ACTIVE (%d) on thread %p", track->name(), this);
}
// set retry count for buffer fill
track->mRetryCount = kMaxTrackStartupRetries;
bool AudioFlinger::MixerThread::threadLoop()
{
- unsigned long sleepTime = 0;
+ uint32_t sleepTime = 0;
+ uint32_t maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs();
int16_t* curBuf = mMixBuffer;
Vector< sp<Track> > tracksToRemove;
size_t enabledTracks = 0;
// FIXME: Relaxed timing because of a certain device that can't meet latency
// Should be reduced to 2x after the vendor fixes the driver issue
maxPeriod = seconds(mFrameCount) / mSampleRate * 3;
+ maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs();
}
const SortedVector< wp<Track> >& activeTracks = mActiveTracks;
}
standbyTime = systemTime() + kStandbyTimeInNsecs;
- sleepTime = 0;
continue;
}
}
sleepTime = 0;
standbyTime = systemTime() + kStandbyTimeInNsecs;
} else {
- sleepTime += kBufferRecoveryInUsecs;
- if (sleepTime > kMaxBufferRecoveryInUsecs) {
- sleepTime = kMaxBufferRecoveryInUsecs;
- }
- // There was nothing to mix this round, which means all
- // active tracks were late. Sleep a little bit to give
- // them another chance. If we're too late, write 0s to audio
- // hardware to avoid underrun.
- if (mBytesWritten != 0 && sleepTime >= kMaxBufferRecoveryInUsecs) {
+ // If no tracks are ready, sleep once for the duration of an output
+ // buffer size, then write 0s to the output
+ if (sleepTime == 0) {
+ sleepTime = maxBufferRecoveryInUsecs;
+ } else if (mBytesWritten != 0) {
memset (curBuf, 0, mixBufferSize);
sleepTime = 0;
}
}
if (mSuspended) {
- sleepTime = kMaxBufferRecoveryInUsecs;
+ sleepTime = maxBufferRecoveryInUsecs;
}
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
mLastWriteTime = systemTime();
mInWrite = true;
- LOGV("mOutput->write() thread %p frames %d", this, mFrameCount);
int bytesWritten = (int)mOutput->write(curBuf, mixBufferSize);
if (bytesWritten > 0) mBytesWritten += bytesWritten;
mNumWrites++;
// No buffers for this track. Give it a few chances to
// fill a buffer, then remove it from active list.
if (--(track->mRetryCount) <= 0) {
- LOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
+ LOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", track->name(), this);
tracksToRemove->add(track);
}
// For tracks using static shared memory buffer, make sure that we have
return NO_ERROR;
}
+uint32_t AudioFlinger::MixerThread::getMaxBufferRecoveryInUsecs()
+{
+ uint32_t time = ((mFrameCount * 1000) / mSampleRate) * 1000;
+ // Add some margin with regard to scheduling precision
+ if (time > 10000) {
+ time -= 10000;
+ }
+ return time;
+}
+
// ----------------------------------------------------------------------------
AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output)
: PlaybackThread(audioFlinger, output),
bool AudioFlinger::DirectOutputThread::threadLoop()
{
- unsigned long sleepTime = 0;
+ uint32_t sleepTime = 0;
+ uint32_t maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs();
sp<Track> trackToRemove;
sp<Track> activeTrack;
nsecs_t standbyTime = systemTime();
if (checkForNewParameters_l()) {
mixBufferSize = mFrameCount*mFrameSize;
+ maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs();
}
// put audio hardware into standby after short delay
}
standbyTime = systemTime() + kStandbyTimeInNsecs;
- sleepTime = 0;
continue;
}
}
sleepTime = 0;
standbyTime = systemTime() + kStandbyTimeInNsecs;
} else {
- sleepTime += kBufferRecoveryInUsecs;
- if (sleepTime > kMaxBufferRecoveryInUsecs) {
- sleepTime = kMaxBufferRecoveryInUsecs;
- }
- // There was nothing to mix this round, which means all
- // active tracks were late. Sleep a little bit to give
- // them another chance. If we're too late, write 0s to audio
- // hardware to avoid underrun.
- if (mBytesWritten != 0 && sleepTime >= kMaxBufferRecoveryInUsecs &&
- AudioSystem::isLinearPCM(mFormat)) {
+ if (sleepTime == 0) {
+ sleepTime = maxBufferRecoveryInUsecs;
+ } else if (mBytesWritten != 0 && AudioSystem::isLinearPCM(mFormat)) {
memset (mMixBuffer, 0, mFrameCount * mFrameSize);
sleepTime = 0;
}
}
if (mSuspended) {
- sleepTime = kMaxBufferRecoveryInUsecs;
+ sleepTime = maxBufferRecoveryInUsecs;
}
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
return reconfig;
}
+uint32_t AudioFlinger::DirectOutputThread::getMaxBufferRecoveryInUsecs()
+{
+ uint32_t time;
+ if (AudioSystem::isLinearPCM(mFormat)) {
+ time = ((mFrameCount * 1000) / mSampleRate) * 1000;
+ // Add some margin with regard to scheduling precision
+ if (time > 10000) {
+ time -= 10000;
+ }
+ } else {
+ time = 10000;
+ }
+ return time;
+}
+
// ----------------------------------------------------------------------------
AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread)
bool AudioFlinger::DuplicatingThread::threadLoop()
{
- unsigned long sleepTime = kBufferRecoveryInUsecs;
+ uint32_t sleepTime = 0;
+ uint32_t maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs();
int16_t* curBuf = mMixBuffer;
Vector< sp<Track> > tracksToRemove;
size_t enabledTracks = 0;
nsecs_t standbyTime = systemTime();
size_t mixBufferSize = mFrameCount*mFrameSize;
SortedVector< sp<OutputTrack> > outputTracks;
+ uint32_t writeFrames = 0;
while (!exitPending())
{
if (checkForNewParameters_l()) {
mixBufferSize = mFrameCount*mFrameSize;
+ maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs();
}
const SortedVector< wp<Track> >& activeTracks = mActiveTracks;
}
standbyTime = systemTime() + kStandbyTimeInNsecs;
- sleepTime = kBufferRecoveryInUsecs;
continue;
}
}
// mix buffers...
mAudioMixer->process(curBuf);
sleepTime = 0;
- standbyTime = systemTime() + kStandbyTimeInNsecs;
+ writeFrames = mFrameCount;
} else {
- sleepTime += kBufferRecoveryInUsecs;
- if (sleepTime > kMaxBufferRecoveryInUsecs) {
- sleepTime = kMaxBufferRecoveryInUsecs;
- }
- // There was nothing to mix this round, which means all
- // active tracks were late. Sleep a little bit to give
- // them another chance. If we're too late, write 0s to audio
- // hardware to avoid underrun.
- if (mBytesWritten != 0 && sleepTime >= kMaxBufferRecoveryInUsecs) {
- memset (curBuf, 0, mixBufferSize);
- sleepTime = 0;
+ if (sleepTime == 0) {
+ sleepTime = maxBufferRecoveryInUsecs;
+ } else if (mBytesWritten != 0) {
+ // flush remaining overflow buffers in output tracks
+ for (size_t i = 0; i < outputTracks.size(); i++) {
+ if (outputTracks[i]->isActive()) {
+ sleepTime = 0;
+ writeFrames = 0;
+ break;
+ }
+ }
}
}
if (mSuspended) {
- sleepTime = kMaxBufferRecoveryInUsecs;
+ sleepTime = maxBufferRecoveryInUsecs;
}
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
+ standbyTime = systemTime() + kStandbyTimeInNsecs;
for (size_t i = 0; i < outputTracks.size(); i++) {
- outputTracks[i]->write(curBuf, mFrameCount);
+ outputTracks[i]->write(curBuf, writeFrames);
}
mStandby = false;
mBytesWritten += mixBufferSize;
LOGV("removeOutputTrack(): unkonwn thread: %p", thread);
}
-
// ----------------------------------------------------------------------------
// TrackBase constructor must be called with AudioFlinger::mLock held
getNextBuffer_exit:
buffer->raw = 0;
buffer->frameCount = 0;
- LOGV("getNextBuffer() no more data");
+ LOGV("getNextBuffer() no more data for track %d on thread %p", mName, mThread.unsafe_get());
return NOT_ENOUGH_DATA;
}
if (playbackThread->mActiveTracks.indexOf(this) < 0) {
reset();
}
- LOGV("(> STOPPED) => STOPPED (%d)", mName);
+ LOGV("(> STOPPED) => STOPPED (%d) on thread %p", mName, playbackThread);
}
}
}
Mutex::Autolock _l(thread->mLock);
if (mState == ACTIVE || mState == RESUMING) {
mState = PAUSING;
- LOGV("ACTIVE/RESUMING => PAUSING (%d)", mName);
+ LOGV("ACTIVE/RESUMING => PAUSING (%d) on thread %p", mName, thread.get());
}
}
}
uint32_t waitTimeLeftMs = mWaitTimeMs;
- if (!mActive) {
+ if (!mActive && frames != 0) {
start();
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
break;
}
uint32_t waitTimeMs = (uint32_t)ns2ms(systemTime() - startTime);
-// LOGV("OutputTrack::write() waitTimeMs %d waitTimeLeftMs %d", waitTimeMs, waitTimeLeftMs)
+ LOGV("OutputTrack::write() to thread %p waitTimeMs %d waitTimeLeftMs %d", mThread.unsafe_get(), waitTimeMs, waitTimeLeftMs);
if (waitTimeLeftMs >= waitTimeMs) {
waitTimeLeftMs -= waitTimeMs;
} else {
pInBuffer->i16 = pInBuffer->mBuffer;
memset(pInBuffer->raw, 0, frames * channels * sizeof(int16_t));
mBufferQueue.add(pInBuffer);
- } else {
+ } else if (mActive) {
stop();
}
}
////////////////////////////////////////////////////////////////////////////////
bool ToneGenerator::startTone(int toneType, int durationMs) {
bool lResult = false;
+ status_t lStatus;
if ((toneType < 0) || (toneType >= NUM_TONES))
return lResult;
toneType = getToneForRegion(toneType);
mpNewToneDesc = &sToneDescriptors[toneType];
- if (durationMs == -1) {
- mMaxSmp = TONEGEN_INF;
- } else {
- if (durationMs > (int)(TONEGEN_INF / mSamplingRate)) {
- mMaxSmp = (durationMs / 1000) * mSamplingRate;
- } else {
- mMaxSmp = (durationMs * mSamplingRate) / 1000;
+ mDurationMs = durationMs;
+
+ if (mState == TONE_STOPPED) {
+ LOGV("Start waiting for previous tone to stop");
+ lStatus = mWaitCbkCond.waitRelative(mLock, seconds(1));
+ if (lStatus != NO_ERROR) {
+ LOGE("--- start wait for stop timed out, status %d", lStatus);
+ mState = TONE_IDLE;
+ return lResult;
}
- LOGV("startTone, duration limited to %d ms", durationMs);
}
if (mState == TONE_INIT) {
mLock.lock();
if (mState == TONE_STARTING) {
LOGV("Wait for start callback");
- status_t lStatus = mWaitCbkCond.waitRelative(mLock, seconds(1));
+ lStatus = mWaitCbkCond.waitRelative(mLock, seconds(1));
if (lStatus != NO_ERROR) {
LOGE("--- Immediate start timed out, status %d", lStatus);
mState = TONE_IDLE;
}
} else {
LOGV("Delayed start\n");
-
mState = TONE_RESTARTING;
- status_t lStatus = mWaitCbkCond.waitRelative(mLock, seconds(1));
+ lStatus = mWaitCbkCond.waitRelative(mLock, seconds(1));
if (lStatus == NO_ERROR) {
if (mState != TONE_IDLE) {
lResult = true;
mpToneDesc = mpNewToneDesc;
+ if (mDurationMs == -1) {
+ mMaxSmp = TONEGEN_INF;
+ } else {
+ if (mDurationMs > (int)(TONEGEN_INF / mSamplingRate)) {
+ mMaxSmp = (mDurationMs / 1000) * mSamplingRate;
+ } else {
+ mMaxSmp = (mDurationMs * mSamplingRate) / 1000;
+ }
+ LOGV("prepareWave, duration limited to %d ms", mDurationMs);
+ }
+
while (mpToneDesc->segments[segmentIdx].duration) {
// Get total number of sine waves: needed to adapt sine wave gain.
unsigned int lNumWaves = numWaves(segmentIdx);