diff options
Diffstat (limited to 'services/audioflinger/Threads.cpp')
-rw-r--r-- | services/audioflinger/Threads.cpp | 447 |
1 files changed, 291 insertions, 156 deletions
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 4efb3d7..b30fd20 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -86,7 +86,13 @@ #define ALOGVV(a...) do { } while(0) #endif +// TODO: Move these macro/inlines to a header file. #define max(a, b) ((a) > (b) ? (a) : (b)) +template <typename T> +static inline T min(const T& a, const T& b) +{ + return a < b ? a : b; +} namespace android { @@ -1602,13 +1608,19 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac // If you change this calculation, also review the start threshold which is related. if (!(*flags & IAudioFlinger::TRACK_FAST) && audio_is_linear_pcm(format) && sharedBuffer == 0) { + // this must match AudioTrack.cpp calculateMinFrameCount(). + // TODO: Move to a common library uint32_t latencyMs = mOutput->stream->get_latency(mOutput->stream); uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate); if (minBufCount < 2) { minBufCount = 2; } + // For normal mixing tracks, if speed is > 1.0f (normal), AudioTrack + // or the client should compute and pass in a larger buffer request. size_t minFrameCount = - minBufCount * sourceFramesNeeded(sampleRate, mNormalFrameCount, mSampleRate); + minBufCount * sourceFramesNeededWithTimestretch( + sampleRate, mNormalFrameCount, + mSampleRate, AUDIO_TIMESTRETCH_SPEED_NORMAL /*speed*/); if (frameCount < minFrameCount) { // including frameCount == 0 frameCount = minFrameCount; } @@ -3586,21 +3598,17 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // hence the test on (mMixerStatus == MIXER_TRACKS_READY) meaning the track was mixed // during last round size_t desiredFrames; - uint32_t sr = track->sampleRate(); - if (sr == mSampleRate) { - desiredFrames = mNormalFrameCount; - } else { - desiredFrames = sourceFramesNeeded(sr, mNormalFrameCount, mSampleRate); - // add frames already consumed but not yet released by the resampler - // because mAudioTrackServerProxy->framesReady() will include these frames - desiredFrames += mAudioMixer->getUnreleasedFrames(track->name()); -#if 0 - // the minimum track buffer size is normally twice the number of frames necessary - // to fill one buffer and the resampler should not leave more than one buffer worth - // of unreleased frames after each pass, but just in case... - ALOG_ASSERT(desiredFrames <= cblk->frameCount_); -#endif - } + const uint32_t sampleRate = track->mAudioTrackServerProxy->getSampleRate(); + float speed, pitch; + track->mAudioTrackServerProxy->getPlaybackRate(&speed, &pitch); + + desiredFrames = sourceFramesNeededWithTimestretch( + sampleRate, mNormalFrameCount, mSampleRate, speed); + // TODO: ONLY USED FOR LEGACY RESAMPLERS, remove when they are removed. + // add frames already consumed but not yet released by the resampler + // because mAudioTrackServerProxy->framesReady() will include these frames + desiredFrames += mAudioMixer->getUnreleasedFrames(track->name()); + uint32_t minFrames = 1; if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() && (mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) { @@ -3763,6 +3771,17 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac AudioMixer::RESAMPLE, AudioMixer::SAMPLE_RATE, (void *)(uintptr_t)reqSampleRate); + + // set the playback rate as an float array {speed, pitch} + float playbackRate[2]; + track->mAudioTrackServerProxy->getPlaybackRate( + &playbackRate[0] /*speed*/, &playbackRate[1] /*pitch*/); + mAudioMixer->setParameter( + name, + AudioMixer::TIMESTRETCH, + AudioMixer::PLAYBACK_RATE, + playbackRate); + /* * Select the appropriate output buffer for the track. * @@ -5290,7 +5309,6 @@ failed: ; // FIXME mNormalSource } - AudioFlinger::RecordThread::~RecordThread() { if (mFastCapture != 0) { @@ -5594,6 +5612,9 @@ reacquire_wakelock: continue; } + // TODO: This code probably should be moved to RecordTrack. + // TODO: Update the activeTrack buffer converter in case of reconfigure. + enum { OVERRUN_UNKNOWN, OVERRUN_TRUE, @@ -5608,131 +5629,28 @@ reacquire_wakelock: size_t framesOut = activeTrack->mSink.frameCount; LOG_ALWAYS_FATAL_IF((status == OK) != (framesOut > 0)); - int32_t front = activeTrack->mRsmpInFront; - ssize_t filled = rear - front; + // check available frames and handle overrun conditions + // if the record track isn't draining fast enough. + bool hasOverrun; size_t framesIn; - - if (filled < 0) { - // should not happen, but treat like a massive overrun and re-sync - framesIn = 0; - activeTrack->mRsmpInFront = rear; - overrun = OVERRUN_TRUE; - } else if ((size_t) filled <= mRsmpInFrames) { - framesIn = (size_t) filled; - } else { - // client is not keeping up with server, but give it latest data - framesIn = mRsmpInFrames; - activeTrack->mRsmpInFront = front = rear - framesIn; + activeTrack->mResamplerBufferProvider->sync(&framesIn, &hasOverrun); + if (hasOverrun) { overrun = OVERRUN_TRUE; } - if (framesOut == 0 || framesIn == 0) { break; } - if (activeTrack->mResampler == NULL) { - // no resampling - if (framesIn > framesOut) { - framesIn = framesOut; - } else { - framesOut = framesIn; - } - int8_t *dst = activeTrack->mSink.i8; - while (framesIn > 0) { - front &= mRsmpInFramesP2 - 1; - size_t part1 = mRsmpInFramesP2 - front; - if (part1 > framesIn) { - part1 = framesIn; - } - int8_t *src = (int8_t *)mRsmpInBuffer + (front * mFrameSize); - if (mChannelCount == activeTrack->mChannelCount) { - memcpy(dst, src, part1 * mFrameSize); - } else if (mChannelCount == 1) { - upmix_to_stereo_i16_from_mono_i16((int16_t *)dst, (const int16_t *)src, - part1); - } else { - downmix_to_mono_i16_from_stereo_i16((int16_t *)dst, - (const int16_t *)src, part1); - } - dst += part1 * activeTrack->mFrameSize; - front += part1; - framesIn -= part1; - } - activeTrack->mRsmpInFront += framesOut; - - } else { - // resampling - // FIXME framesInNeeded should really be part of resampler API, and should - // depend on the SRC ratio - // to keep mRsmpInBuffer full so resampler always has sufficient input - size_t framesInNeeded; - // FIXME only re-calculate when it changes, and optimize for common ratios - // Do not precompute in/out because floating point is not associative - // e.g. a*b/c != a*(b/c). - const double in(mSampleRate); - const double out(activeTrack->mSampleRate); - framesInNeeded = ceil(framesOut * in / out) + 1; - ALOGV("need %u frames in to produce %u out given in/out ratio of %.4g", - framesInNeeded, framesOut, in / out); - // Although we theoretically have framesIn in circular buffer, some of those are - // unreleased frames, and thus must be discounted for purpose of budgeting. - size_t unreleased = activeTrack->mRsmpInUnrel; - framesIn = framesIn > unreleased ? framesIn - unreleased : 0; - if (framesIn < framesInNeeded) { - ALOGV("not enough to resample: have %u frames in but need %u in to " - "produce %u out given in/out ratio of %.4g", - framesIn, framesInNeeded, framesOut, in / out); - size_t newFramesOut = framesIn > 0 ? floor((framesIn - 1) * out / in) : 0; - LOG_ALWAYS_FATAL_IF(newFramesOut >= framesOut); - if (newFramesOut == 0) { - break; - } - framesInNeeded = ceil(newFramesOut * in / out) + 1; - ALOGV("now need %u frames in to produce %u out given out/in ratio of %.4g", - framesInNeeded, newFramesOut, out / in); - LOG_ALWAYS_FATAL_IF(framesIn < framesInNeeded); - ALOGV("success 2: have %u frames in and need %u in to produce %u out " - "given in/out ratio of %.4g", - framesIn, framesInNeeded, newFramesOut, in / out); - framesOut = newFramesOut; - } else { - ALOGV("success 1: have %u in and need %u in to produce %u out " - "given in/out ratio of %.4g", - framesIn, framesInNeeded, framesOut, in / out); - } - - // reallocate mRsmpOutBuffer as needed; we will grow but never shrink - if (activeTrack->mRsmpOutFrameCount < framesOut) { - // FIXME why does each track need it's own mRsmpOutBuffer? can't they share? - delete[] activeTrack->mRsmpOutBuffer; - // resampler always outputs stereo - activeTrack->mRsmpOutBuffer = new int32_t[framesOut * FCC_2]; - activeTrack->mRsmpOutFrameCount = framesOut; - } - - // resampler accumulates, but we only have one source track - memset(activeTrack->mRsmpOutBuffer, 0, framesOut * FCC_2 * sizeof(int32_t)); - activeTrack->mResampler->resample(activeTrack->mRsmpOutBuffer, framesOut, - // FIXME how about having activeTrack implement this interface itself? - activeTrack->mResamplerBufferProvider - /*this*/ /* AudioBufferProvider* */); - // ditherAndClamp() works as long as all buffers returned by - // activeTrack->getNextBuffer() are 32 bit aligned which should be always true. - if (activeTrack->mChannelCount == 1) { - // temporarily type pun mRsmpOutBuffer from Q4.27 to int16_t - ditherAndClamp(activeTrack->mRsmpOutBuffer, activeTrack->mRsmpOutBuffer, - framesOut); - // the resampler always outputs stereo samples: - // do post stereo to mono conversion - downmix_to_mono_i16_from_stereo_i16(activeTrack->mSink.i16, - (const int16_t *)activeTrack->mRsmpOutBuffer, framesOut); - } else { - ditherAndClamp((int32_t *)activeTrack->mSink.raw, - activeTrack->mRsmpOutBuffer, framesOut); - } - // now done with mRsmpOutBuffer - - } + // Don't allow framesOut to be larger than what is possible with resampling + // from framesIn. + // This isn't strictly necessary but helps limit buffer resizing in + // RecordBufferConverter. TODO: remove when no longer needed. + framesOut = min(framesOut, + destinationFramesPossible( + framesIn, mSampleRate, activeTrack->mSampleRate)); + // process frames from the RecordThread buffer provider to the RecordTrack buffer + framesOut = activeTrack->mRecordBufferConverter->convert( + activeTrack->mSink.raw, activeTrack->mResamplerBufferProvider, framesOut); if (framesOut > 0 && (overrun == OVERRUN_UNKNOWN)) { overrun = OVERRUN_FALSE; @@ -6041,12 +5959,9 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac // was initialized to some value closer to the thread's mRsmpInFront, then the track could // see previously buffered data before it called start(), but with greater risk of overrun. - recordTrack->mRsmpInFront = mRsmpInRear; - recordTrack->mRsmpInUnrel = 0; - // FIXME why reset? - if (recordTrack->mResampler != NULL) { - recordTrack->mResampler->reset(); - } + recordTrack->mResamplerBufferProvider->reset(); + // clear any converter state as new data will be discontinuous + recordTrack->mRecordBufferConverter->reset(); recordTrack->mState = TrackBase::STARTING_2; // signal thread to start mWaitWorkCV.broadcast(); @@ -6222,12 +6137,52 @@ void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args write(fd, result.string(), result.size()); } + +void AudioFlinger::RecordThread::ResamplerBufferProvider::reset() +{ + sp<ThreadBase> threadBase = mRecordTrack->mThread.promote(); + RecordThread *recordThread = (RecordThread *) threadBase.get(); + mRsmpInFront = recordThread->mRsmpInRear; + mRsmpInUnrel = 0; +} + +void AudioFlinger::RecordThread::ResamplerBufferProvider::sync( + size_t *framesAvailable, bool *hasOverrun) +{ + sp<ThreadBase> threadBase = mRecordTrack->mThread.promote(); + RecordThread *recordThread = (RecordThread *) threadBase.get(); + const int32_t rear = recordThread->mRsmpInRear; + const int32_t front = mRsmpInFront; + const ssize_t filled = rear - front; + + size_t framesIn; + bool overrun = false; + if (filled < 0) { + // should not happen, but treat like a massive overrun and re-sync + framesIn = 0; + mRsmpInFront = rear; + overrun = true; + } else if ((size_t) filled <= recordThread->mRsmpInFrames) { + framesIn = (size_t) filled; + } else { + // client is not keeping up with server, but give it latest data + framesIn = recordThread->mRsmpInFrames; + mRsmpInFront = /* front = */ rear - framesIn; + overrun = true; + } + if (framesAvailable != NULL) { + *framesAvailable = framesIn; + } + if (hasOverrun != NULL) { + *hasOverrun = overrun; + } +} + // AudioBufferProvider interface status_t AudioFlinger::RecordThread::ResamplerBufferProvider::getNextBuffer( AudioBufferProvider::Buffer* buffer, int64_t pts __unused) { - RecordTrack *activeTrack = mRecordTrack; - sp<ThreadBase> threadBase = activeTrack->mThread.promote(); + sp<ThreadBase> threadBase = mRecordTrack->mThread.promote(); if (threadBase == 0) { buffer->frameCount = 0; buffer->raw = NULL; @@ -6235,7 +6190,7 @@ status_t AudioFlinger::RecordThread::ResamplerBufferProvider::getNextBuffer( } RecordThread *recordThread = (RecordThread *) threadBase.get(); int32_t rear = recordThread->mRsmpInRear; - int32_t front = activeTrack->mRsmpInFront; + int32_t front = mRsmpInFront; ssize_t filled = rear - front; // FIXME should not be P2 (don't want to increase latency) // FIXME if client not keeping up, discard @@ -6252,17 +6207,16 @@ status_t AudioFlinger::RecordThread::ResamplerBufferProvider::getNextBuffer( part1 = ask; } if (part1 == 0) { - // Higher-level should keep mRsmpInBuffer full, and not call resampler if empty - LOG_ALWAYS_FATAL("RecordThread::getNextBuffer() starved"); + // out of data is fine since the resampler will return a short-count. buffer->raw = NULL; buffer->frameCount = 0; - activeTrack->mRsmpInUnrel = 0; + mRsmpInUnrel = 0; return NOT_ENOUGH_DATA; } buffer->raw = recordThread->mRsmpInBuffer + front * recordThread->mChannelCount; buffer->frameCount = part1; - activeTrack->mRsmpInUnrel = part1; + mRsmpInUnrel = part1; return NO_ERROR; } @@ -6270,18 +6224,197 @@ status_t AudioFlinger::RecordThread::ResamplerBufferProvider::getNextBuffer( void AudioFlinger::RecordThread::ResamplerBufferProvider::releaseBuffer( AudioBufferProvider::Buffer* buffer) { - RecordTrack *activeTrack = mRecordTrack; size_t stepCount = buffer->frameCount; if (stepCount == 0) { return; } - ALOG_ASSERT(stepCount <= activeTrack->mRsmpInUnrel); - activeTrack->mRsmpInUnrel -= stepCount; - activeTrack->mRsmpInFront += stepCount; + ALOG_ASSERT(stepCount <= mRsmpInUnrel); + mRsmpInUnrel -= stepCount; + mRsmpInFront += stepCount; buffer->raw = NULL; buffer->frameCount = 0; } +AudioFlinger::RecordThread::RecordBufferConverter::RecordBufferConverter( + audio_channel_mask_t srcChannelMask, audio_format_t srcFormat, + uint32_t srcSampleRate, + audio_channel_mask_t dstChannelMask, audio_format_t dstFormat, + uint32_t dstSampleRate) : + mSrcChannelMask(AUDIO_CHANNEL_INVALID), // updateParameters will set following vars + // mSrcFormat + // mSrcSampleRate + // mDstChannelMask + // mDstFormat + // mDstSampleRate + // mSrcChannelCount + // mDstChannelCount + // mDstFrameSize + mBuf(NULL), mBufFrames(0), mBufFrameSize(0), + mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0) +{ + (void)updateParameters(srcChannelMask, srcFormat, srcSampleRate, + dstChannelMask, dstFormat, dstSampleRate); +} + +AudioFlinger::RecordThread::RecordBufferConverter::~RecordBufferConverter() { + free(mBuf); + delete mResampler; + free(mRsmpOutBuffer); +} + +size_t AudioFlinger::RecordThread::RecordBufferConverter::convert(void *dst, + AudioBufferProvider *provider, size_t frames) +{ + if (mSrcSampleRate == mDstSampleRate) { + ALOGVV("NO RESAMPLING sampleRate:%u mSrcFormat:%#x mDstFormat:%#x", + mSrcSampleRate, mSrcFormat, mDstFormat); + + AudioBufferProvider::Buffer buffer; + for (size_t i = frames; i > 0; ) { + buffer.frameCount = i; + status_t status = provider->getNextBuffer(&buffer, 0); + if (status != OK || buffer.frameCount == 0) { + frames -= i; // cannot fill request. + break; + } + // convert to destination buffer + convert(dst, buffer.raw, buffer.frameCount); + + dst = (int8_t*)dst + buffer.frameCount * mDstFrameSize; + i -= buffer.frameCount; + provider->releaseBuffer(&buffer); + } + } else { + ALOGVV("RESAMPLING mSrcSampleRate:%u mDstSampleRate:%u mSrcFormat:%#x mDstFormat:%#x", + mSrcSampleRate, mDstSampleRate, mSrcFormat, mDstFormat); + + // reallocate mRsmpOutBuffer as needed; we will grow but never shrink + if (mRsmpOutFrameCount < frames) { + // FIXME why does each track need it's own mRsmpOutBuffer? can't they share? + free(mRsmpOutBuffer); + // resampler always outputs stereo (FOR NOW) + (void)posix_memalign(&mRsmpOutBuffer, 32, frames * FCC_2 * sizeof(int32_t) /*Q4.27*/); + mRsmpOutFrameCount = frames; + } + // resampler accumulates, but we only have one source track + memset(mRsmpOutBuffer, 0, frames * FCC_2 * sizeof(int32_t)); + frames = mResampler->resample((int32_t*)mRsmpOutBuffer, frames, provider); + + // convert to destination buffer + convert(dst, mRsmpOutBuffer, frames); + } + return frames; +} + +status_t AudioFlinger::RecordThread::RecordBufferConverter::updateParameters( + audio_channel_mask_t srcChannelMask, audio_format_t srcFormat, + uint32_t srcSampleRate, + audio_channel_mask_t dstChannelMask, audio_format_t dstFormat, + uint32_t dstSampleRate) +{ + // quick evaluation if there is any change. + if (mSrcFormat == srcFormat + && mSrcChannelMask == srcChannelMask + && mSrcSampleRate == srcSampleRate + && mDstFormat == dstFormat + && mDstChannelMask == dstChannelMask + && mDstSampleRate == dstSampleRate) { + return NO_ERROR; + } + + const bool valid = + audio_is_input_channel(srcChannelMask) + && audio_is_input_channel(dstChannelMask) + && audio_is_valid_format(srcFormat) && audio_is_linear_pcm(srcFormat) + && audio_is_valid_format(dstFormat) && audio_is_linear_pcm(dstFormat) + && (srcSampleRate <= dstSampleRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX) + ; // no upsampling checks for now + if (!valid) { + return BAD_VALUE; + } + + mSrcFormat = srcFormat; + mSrcChannelMask = srcChannelMask; + mSrcSampleRate = srcSampleRate; + mDstFormat = dstFormat; + mDstChannelMask = dstChannelMask; + mDstSampleRate = dstSampleRate; + + // compute derived parameters + mSrcChannelCount = audio_channel_count_from_in_mask(srcChannelMask); + mDstChannelCount = audio_channel_count_from_in_mask(dstChannelMask); + mDstFrameSize = mDstChannelCount * audio_bytes_per_sample(mDstFormat); + + // do we need a format buffer? + if (mSrcFormat != mDstFormat && mDstChannelCount != mSrcChannelCount) { + mBufFrameSize = mDstChannelCount * audio_bytes_per_sample(mSrcFormat); + } else { + mBufFrameSize = 0; + } + mBufFrames = 0; // force the buffer to be resized. + + // do we need to resample? + if (mSrcSampleRate != mDstSampleRate) { + if (mResampler != NULL) { + delete mResampler; + } + mResampler = AudioResampler::create(AUDIO_FORMAT_PCM_16_BIT, + mSrcChannelCount, mDstSampleRate); // may seem confusing... + mResampler->setSampleRate(mSrcSampleRate); + mResampler->setVolume(AudioMixer::UNITY_GAIN_FLOAT, AudioMixer::UNITY_GAIN_FLOAT); + } + return NO_ERROR; +} + +void AudioFlinger::RecordThread::RecordBufferConverter::convert( + void *dst, /*const*/ void *src, size_t frames) +{ + // check if a memcpy will do + if (mResampler == NULL + && mSrcChannelCount == mDstChannelCount + && mSrcFormat == mDstFormat) { + memcpy(dst, src, + frames * mDstChannelCount * audio_bytes_per_sample(mDstFormat)); + return; + } + // reallocate buffer if needed + if (mBufFrameSize != 0 && mBufFrames < frames) { + free(mBuf); + mBufFrames = frames; + (void)posix_memalign(&mBuf, 32, mBufFrames * mBufFrameSize); + } + // do processing + if (mResampler != NULL) { + // src channel count is always >= 2. + void *dstBuf = mBuf != NULL ? mBuf : dst; + // ditherAndClamp() works as long as all buffers returned by + // activeTrack->getNextBuffer() are 32 bit aligned which should be always true. + if (mDstChannelCount == 1) { + // the resampler always outputs stereo samples. + // FIXME: this rewrites back into src + ditherAndClamp((int32_t *)src, (const int32_t *)src, frames); + downmix_to_mono_i16_from_stereo_i16((int16_t *)dstBuf, + (const int16_t *)src, frames); + } else { + ditherAndClamp((int32_t *)dstBuf, (const int32_t *)src, frames); + } + } else if (mSrcChannelCount != mDstChannelCount) { + void *dstBuf = mBuf != NULL ? mBuf : dst; + if (mSrcChannelCount == 1) { + upmix_to_stereo_i16_from_mono_i16((int16_t *)dstBuf, (const int16_t *)src, + frames); + } else { + downmix_to_mono_i16_from_stereo_i16((int16_t *)dstBuf, + (const int16_t *)src, frames); + } + } + if (mSrcFormat != mDstFormat) { + void *srcBuf = mBuf != NULL ? mBuf : src; + memcpy_by_audio_format(dst, mDstFormat, srcBuf, mSrcFormat, + frames * mDstChannelCount); + } +} + bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValuePair, status_t& status) { @@ -6303,7 +6436,7 @@ bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValueP reconfig = true; } if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) { - if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) { + if (!audio_is_linear_pcm((audio_format_t) value)) { status = BAD_VALUE; } else { reqFormat = (audio_format_t) value; @@ -6377,10 +6510,10 @@ bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValueP } if (reconfig) { if (status == BAD_VALUE && - reqFormat == mInput->stream->common.get_format(&mInput->stream->common) && - reqFormat == AUDIO_FORMAT_PCM_16_BIT && + audio_is_linear_pcm(mInput->stream->common.get_format(&mInput->stream->common)) && + audio_is_linear_pcm(reqFormat) && (mInput->stream->common.get_sample_rate(&mInput->stream->common) - <= (2 * samplingRate)) && + <= (AUDIO_RESAMPLER_DOWN_RATIO_MAX * samplingRate)) && audio_channel_count_from_in_mask( mInput->stream->common.get_channels(&mInput->stream->common)) <= FCC_2 && (channelMask == AUDIO_CHANNEL_IN_MONO || @@ -6451,6 +6584,8 @@ void AudioFlinger::RecordThread::readInputParameters_l() // The value is somewhat arbitrary, and could probably be even larger. // A larger value should allow more old data to be read after a track calls start(), // without increasing latency. + // + // Note this is independent of the maximum downsampling ratio permitted for capture. mRsmpInFrames = mFrameCount * 7; mRsmpInFramesP2 = roundup(mRsmpInFrames); delete[] mRsmpInBuffer; |