summaryrefslogtreecommitdiffstats
path: root/services/audioflinger/Threads.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'services/audioflinger/Threads.cpp')
-rw-r--r--services/audioflinger/Threads.cpp447
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;