Pull HAL reads out of RecordThread resample
Previously, the resample was indirectly calling HAL read
from within the BufferProvider's getNextBuffer. Now the HAL
read is done ahead of time into a circular buffer,
and the getNextBuffer just reads out of that memory.
This change will permit some future planned features.
Also removed the mono to mono resampling optimization,
as that optimization will no longer be valid in the future.
Change-Id: I59f5016a2df91078d697c37a7dec2d9d9f44542e
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 79c41f7..c9c9f8a 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1717,9 +1717,11 @@
reqFormat == config.format && config.format == AUDIO_FORMAT_PCM_16_BIT &&
(config.sample_rate <= 2 * reqSamplingRate) &&
(popcount(config.channel_mask) <= FCC_2) && (popcount(reqChannelMask) <= FCC_2)) {
+ // FIXME describe the change proposed by HAL (save old values so we can log them here)
ALOGV("openInput() reopening with proposed sampling rate and channel mask");
inStream = NULL;
status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config, &inStream);
+ // FIXME log this new status; HAL should not propose any further changes
}
if (status == NO_ERROR && inStream != NULL) {
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 7474f1a..17e2a57 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -4378,7 +4378,9 @@
) :
ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD),
mInput(input), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
- // mRsmpInIndex set by readInputParameters()
+ // mRsmpInFrames, mRsmpInFramesP2, mRsmpInUnrel, mRsmpInFront, and mRsmpInRear
+ // are set by readInputParameters()
+ // mRsmpInIndex LEGACY
mReqChannelCount(popcount(channelMask)),
mReqSampleRate(sampleRate)
// mBytesRead is only meaningful while active, and so is cleared in start()
@@ -4583,17 +4585,60 @@
} else {
// resampling
+ // avoid busy-waiting if client doesn't keep up
+ bool madeProgress = false;
+
+ // keep mRsmpInBuffer full so resampler always has sufficient input
+ for (;;) {
+ int32_t rear = mRsmpInRear;
+ ssize_t filled = rear - mRsmpInFront;
+ ALOG_ASSERT(0 <= filled && (size_t) filled <= mRsmpInFramesP2);
+ // exit once there is enough data in buffer for resampler
+ if ((size_t) filled >= mRsmpInFrames) {
+ break;
+ }
+ size_t avail = mRsmpInFramesP2 - filled;
+ // Only try to read full HAL buffers.
+ // But if the HAL read returns a partial buffer, use it.
+ if (avail < mFrameCount) {
+ ALOGE("insufficient space to read: avail %d < mFrameCount %d",
+ avail, mFrameCount);
+ break;
+ }
+ // If 'avail' is non-contiguous, first read past the nominal end of buffer, then
+ // copy to the right place. Permitted because mRsmpInBuffer was over-allocated.
+ rear &= mRsmpInFramesP2 - 1;
+ mBytesRead = mInput->stream->read(mInput->stream,
+ &mRsmpInBuffer[rear * mChannelCount], mBufferSize);
+ if (mBytesRead <= 0) {
+ ALOGE("read failed: mBytesRead=%d < %u", mBytesRead, mBufferSize);
+ break;
+ }
+ ALOG_ASSERT((size_t) mBytesRead <= mBufferSize);
+ size_t framesRead = mBytesRead / mFrameSize;
+ ALOG_ASSERT(framesRead > 0);
+ madeProgress = true;
+ // If 'avail' was non-contiguous, we now correct for reading past end of buffer.
+ size_t part1 = mRsmpInFramesP2 - rear;
+ if (framesRead > part1) {
+ memcpy(mRsmpInBuffer, &mRsmpInBuffer[mRsmpInFramesP2 * mChannelCount],
+ (framesRead - part1) * mFrameSize);
+ }
+ mRsmpInRear += framesRead;
+ }
+
+ if (!madeProgress) {
+ ALOGV("Did not make progress");
+ usleep(((mFrameCount * 1000) / mSampleRate) * 1000);
+ }
+
// resampler accumulates, but we only have one source track
memset(mRsmpOutBuffer, 0, framesOut * FCC_2 * sizeof(int32_t));
- // alter output frame count as if we were expecting stereo samples
- if (mChannelCount == 1 && mReqChannelCount == 1) {
- framesOut >>= 1;
- }
mResampler->resample(mRsmpOutBuffer, framesOut,
this /* AudioBufferProvider* */);
// ditherAndClamp() works as long as all buffers returned by
// activeTrack->getNextBuffer() are 32 bit aligned which should be always true.
- if (mChannelCount == 2 && mReqChannelCount == 1) {
+ if (mReqChannelCount == 1) {
// temporarily type pun mRsmpOutBuffer from Q19.12 to int16_t
ditherAndClamp(mRsmpOutBuffer, mRsmpOutBuffer, framesOut);
// the resampler always outputs stereo samples:
@@ -4836,7 +4881,11 @@
clearSyncStartEvent();
return status;
}
+ // FIXME LEGACY
mRsmpInIndex = mFrameCount;
+ mRsmpInFront = 0;
+ mRsmpInRear = 0;
+ mRsmpInUnrel = 0;
mBytesRead = 0;
if (mResampler != NULL) {
mResampler->reset();
@@ -5034,47 +5083,47 @@
// AudioBufferProvider interface
status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts)
{
- size_t framesReq = buffer->frameCount;
- size_t framesReady = mFrameCount - mRsmpInIndex;
- int channelCount;
-
- if (framesReady == 0) {
- mBytesRead = mInput->stream->read(mInput->stream, mRsmpInBuffer, mBufferSize);
- if (mBytesRead <= 0) {
- if ((mBytesRead < 0) && (mActiveTrack->mState == TrackBase::ACTIVE)) {
- ALOGE("RecordThread::getNextBuffer() Error reading audio input");
- // Force input into standby so that it tries to
- // recover at next read attempt
- inputStandBy();
- // FIXME an awkward place to sleep, consider using doSleep when this is pulled up
- usleep(kRecordThreadSleepUs);
- }
- buffer->raw = NULL;
- buffer->frameCount = 0;
- return NOT_ENOUGH_DATA;
- }
- mRsmpInIndex = 0;
- framesReady = mFrameCount;
+ int32_t rear = mRsmpInRear;
+ int32_t front = mRsmpInFront;
+ ssize_t filled = rear - front;
+ ALOG_ASSERT(0 <= filled && (size_t) filled <= mRsmpInFramesP2);
+ // 'filled' may be non-contiguous, so return only the first contiguous chunk
+ front &= mRsmpInFramesP2 - 1;
+ size_t part1 = mRsmpInFramesP2 - front;
+ if (part1 > (size_t) filled) {
+ part1 = filled;
+ }
+ size_t ask = buffer->frameCount;
+ ALOG_ASSERT(ask > 0);
+ if (part1 > ask) {
+ part1 = ask;
+ }
+ if (part1 == 0) {
+ // Higher-level should keep mRsmpInBuffer full, and not call resampler if empty
+ ALOGE("RecordThread::getNextBuffer() starved");
+ buffer->raw = NULL;
+ buffer->frameCount = 0;
+ mRsmpInUnrel = 0;
+ return NOT_ENOUGH_DATA;
}
- if (framesReq > framesReady) {
- framesReq = framesReady;
- }
-
- if (mChannelCount == 1 && mReqChannelCount == 2) {
- channelCount = 1;
- } else {
- channelCount = 2;
- }
- buffer->raw = mRsmpInBuffer + mRsmpInIndex * channelCount;
- buffer->frameCount = framesReq;
+ buffer->raw = mRsmpInBuffer + front * mChannelCount;
+ buffer->frameCount = part1;
+ mRsmpInUnrel = part1;
return NO_ERROR;
}
// AudioBufferProvider interface
void AudioFlinger::RecordThread::releaseBuffer(AudioBufferProvider::Buffer* buffer)
{
- mRsmpInIndex += buffer->frameCount;
+ size_t stepCount = buffer->frameCount;
+ if (stepCount == 0) {
+ return;
+ }
+ ALOG_ASSERT(stepCount <= mRsmpInUnrel);
+ mRsmpInUnrel -= stepCount;
+ mRsmpInFront += stepCount;
+ buffer->raw = NULL;
buffer->frameCount = 0;
}
@@ -5252,28 +5301,22 @@
mFrameSize = audio_stream_frame_size(&mInput->stream->common);
mBufferSize = mInput->stream->common.get_buffer_size(&mInput->stream->common);
mFrameCount = mBufferSize / mFrameSize;
- mRsmpInBuffer = new int16_t[mFrameCount * mChannelCount];
+ // With 3 HAL buffers, we can guarantee ability to down-sample the input by ratio of 2:1 to
+ // 1 full output buffer, regardless of the alignment of the available input.
+ mRsmpInFrames = mFrameCount * 3;
+ mRsmpInFramesP2 = roundup(mRsmpInFrames);
+ // Over-allocate beyond mRsmpInFramesP2 to permit a HAL read past end of buffer
+ mRsmpInBuffer = new int16_t[(mRsmpInFramesP2 + mFrameCount - 1) * mChannelCount];
+ mRsmpInFront = 0;
+ mRsmpInRear = 0;
+ mRsmpInUnrel = 0;
if (mSampleRate != mReqSampleRate && mChannelCount <= FCC_2 && mReqChannelCount <= FCC_2) {
- uint32_t channelCount;
- // optimization: if mono to mono, use the resampler in stereo to stereo mode to avoid
- // stereo to mono post process as the resampler always outputs stereo.
- if (mChannelCount == 1 && mReqChannelCount == 2) {
- channelCount = 1;
- } else {
- channelCount = 2;
- }
mResampler = AudioResampler::create(16, (int) channelCount, mReqSampleRate);
mResampler->setSampleRate(mSampleRate);
mResampler->setVolume(AudioMixer::UNITY_GAIN, AudioMixer::UNITY_GAIN);
+ // resampler always outputs stereo
mRsmpOutBuffer = new int32_t[mFrameCount * FCC_2];
-
- // optmization: if mono to mono, alter input frame count as if we were inputing
- // stereo samples
- if (mChannelCount == 1 && mReqChannelCount == 1) {
- mFrameCount >>= 1;
- }
-
}
mRsmpInIndex = mFrameCount;
}
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index cbc3827..43e335d 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -947,10 +947,22 @@
AudioResampler *mResampler;
// interleaved stereo pairs of fixed-point signed Q19.12
int32_t *mRsmpOutBuffer;
- int16_t *mRsmpInBuffer; // [mFrameCount * mChannelCount]
- size_t mRsmpInIndex;
+
+ // resampler converts input at HAL Hz to output at AudioRecord client Hz
+ int16_t *mRsmpInBuffer; // see new[] for details on the size
+ size_t mRsmpInFrames; // size of resampler input in frames
+ size_t mRsmpInFramesP2;// size rounded up to a power-of-2
+ size_t mRsmpInUnrel; // unreleased frames remaining from
+ // most recent getNextBuffer
+ // these are rolling counters that are never cleared
+ int32_t mRsmpInFront; // next available frame
+ int32_t mRsmpInRear; // last filled frame + 1
+ size_t mRsmpInIndex; // FIXME legacy
+
+ // client's requested configuration, which may differ from the HAL configuration
const uint32_t mReqChannelCount;
const uint32_t mReqSampleRate;
+
ssize_t mBytesRead;
// sync event triggering actual audio capture. Frames read before this event will
// be dropped and therefore not read by the application.