summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorAndy Hung <hunga@google.com>2015-04-20 13:23:41 -0700
committerAndy Hung <hunga@google.com>2015-04-22 12:42:52 -0700
commitd330ee46022f34da76d14d0c4d2910526ecc2321 (patch)
tree05df908a6cd115a92eb1e15a2daa191c14a441b2 /services
parentf27e2fbfc3284c00a60fa68edc51d436f75b1e32 (diff)
downloadframeworks_av-d330ee46022f34da76d14d0c4d2910526ecc2321.zip
frameworks_av-d330ee46022f34da76d14d0c4d2910526ecc2321.tar.gz
frameworks_av-d330ee46022f34da76d14d0c4d2910526ecc2321.tar.bz2
Add floating and multichannel record to AudioFlinger
Change-Id: Ia388fb012a0b6d81613ef87142a97d76836338f9
Diffstat (limited to 'services')
-rw-r--r--services/audioflinger/AudioFlinger.h12
-rw-r--r--services/audioflinger/FastCapture.cpp2
-rw-r--r--services/audioflinger/Threads.cpp211
-rw-r--r--services/audioflinger/Threads.h17
4 files changed, 162 insertions, 80 deletions
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index c7d9161..e1ddcbc 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -73,18 +73,18 @@ class AudioMixer;
class AudioBuffer;
class AudioResampler;
class FastMixer;
+class PassthruBufferProvider;
class ServerProxy;
// ----------------------------------------------------------------------------
-// AudioFlinger has a hard-coded upper limit of 2 channels for capture and playback.
-// There is support for > 2 channel tracks down-mixed to 2 channel output via a down-mix effect.
-// Adding full support for > 2 channel capture or playback would require more than simply changing
-// this #define. There is an independent hard-coded upper limit in AudioMixer;
-// removing that AudioMixer limit would be necessary but insufficient to support > 2 channels.
-// The macro FCC_2 highlights some (but not all) places where there is are 2-channel assumptions.
+// The macro FCC_2 highlights some (but not all) places where there are are 2-channel assumptions.
+// This is typically due to legacy implementation of stereo input or output.
// Search also for "2", "left", "right", "[0]", "[1]", ">> 16", "<< 16", etc.
#define FCC_2 2 // FCC_2 = Fixed Channel Count 2
+// The macro FCC_8 highlights places where there are 8-channel assumptions.
+// This is typically due to audio mixer and resampler limitations.
+#define FCC_8 8 // FCC_8 = Fixed Channel Count 8
static const nsecs_t kDefaultStandbyTimeInNsecs = seconds(3);
diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp
index 9e7e8a4..79ac12b 100644
--- a/services/audioflinger/FastCapture.cpp
+++ b/services/audioflinger/FastCapture.cpp
@@ -105,7 +105,7 @@ void FastCapture::onStateChange()
mFormat = mInputSource->format();
mSampleRate = Format_sampleRate(mFormat);
unsigned channelCount = Format_channelCount(mFormat);
- ALOG_ASSERT(channelCount == 1 || channelCount == 2);
+ ALOG_ASSERT(channelCount >= 1 && channelCount <= FCC_8);
}
dumpState->mSampleRate = mSampleRate;
eitherChanged = true;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index d3d77cd..4039564 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -56,6 +56,7 @@
#include "AudioFlinger.h"
#include "AudioMixer.h"
+#include "BufferProviders.h"
#include "FastMixer.h"
#include "FastCapture.h"
#include "ServiceUtilities.h"
@@ -94,6 +95,10 @@ static inline T min(const T& a, const T& b)
return a < b ? a : b;
}
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+#endif
+
namespace android {
// retry counts for buffer fill timeout
@@ -6246,7 +6251,11 @@ AudioFlinger::RecordThread::RecordBufferConverter::RecordBufferConverter(
// mDstChannelCount
// mDstFrameSize
mBuf(NULL), mBufFrames(0), mBufFrameSize(0),
- mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0)
+ mResampler(NULL),
+ mIsLegacyDownmix(false),
+ mIsLegacyUpmix(false),
+ mRequiresFloat(false),
+ mInputConverterProvider(NULL)
{
(void)updateParameters(srcChannelMask, srcFormat, srcSampleRate,
dstChannelMask, dstFormat, dstSampleRate);
@@ -6255,13 +6264,18 @@ AudioFlinger::RecordThread::RecordBufferConverter::RecordBufferConverter(
AudioFlinger::RecordThread::RecordBufferConverter::~RecordBufferConverter() {
free(mBuf);
delete mResampler;
- free(mRsmpOutBuffer);
+ delete mInputConverterProvider;
}
size_t AudioFlinger::RecordThread::RecordBufferConverter::convert(void *dst,
AudioBufferProvider *provider, size_t frames)
{
- if (mSrcSampleRate == mDstSampleRate) {
+ if (mInputConverterProvider != NULL) {
+ mInputConverterProvider->setBufferProvider(provider);
+ provider = mInputConverterProvider;
+ }
+
+ if (mResampler == NULL) {
ALOGVV("NO RESAMPLING sampleRate:%u mSrcFormat:%#x mDstFormat:%#x",
mSrcSampleRate, mSrcFormat, mDstFormat);
@@ -6273,8 +6287,8 @@ size_t AudioFlinger::RecordThread::RecordBufferConverter::convert(void *dst,
frames -= i; // cannot fill request.
break;
}
- // convert to destination buffer
- convert(dst, buffer.raw, buffer.frameCount);
+ // format convert to destination buffer
+ convertNoResampler(dst, buffer.raw, buffer.frameCount);
dst = (int8_t*)dst + buffer.frameCount * mDstFrameSize;
i -= buffer.frameCount;
@@ -6284,20 +6298,17 @@ size_t AudioFlinger::RecordThread::RecordBufferConverter::convert(void *dst,
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;
- }
+ // reallocate buffer if needed
+ if (mBufFrameSize != 0 && mBufFrames < frames) {
+ free(mBuf);
+ mBufFrames = frames;
+ (void)posix_memalign(&mBuf, 32, mBufFrames * mBufFrameSize);
+ }
// 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);
+ memset(mBuf, 0, frames * mBufFrameSize);
+ frames = mResampler->resample((int32_t*)mBuf, frames, provider);
+ // format convert to destination buffer
+ convertResampler(dst, mBuf, frames);
}
return frames;
}
@@ -6341,74 +6352,132 @@ status_t AudioFlinger::RecordThread::RecordBufferConverter::updateParameters(
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) {
+ // do we need to resample?
+ delete mResampler;
+ mResampler = NULL;
+ if (mSrcSampleRate != mDstSampleRate) {
+ mResampler = AudioResampler::create(AUDIO_FORMAT_PCM_FLOAT,
+ mSrcChannelCount, mDstSampleRate);
+ mResampler->setSampleRate(mSrcSampleRate);
+ mResampler->setVolume(AudioMixer::UNITY_GAIN_FLOAT, AudioMixer::UNITY_GAIN_FLOAT);
+ }
+
+ // are we running legacy channel conversion modes?
+ mIsLegacyDownmix = (mSrcChannelMask == AUDIO_CHANNEL_IN_STEREO
+ || mSrcChannelMask == AUDIO_CHANNEL_IN_FRONT_BACK)
+ && mDstChannelMask == AUDIO_CHANNEL_IN_MONO;
+ mIsLegacyUpmix = mSrcChannelMask == AUDIO_CHANNEL_IN_MONO
+ && (mDstChannelMask == AUDIO_CHANNEL_IN_STEREO
+ || mDstChannelMask == AUDIO_CHANNEL_IN_FRONT_BACK);
+
+ // do we need to process in float?
+ mRequiresFloat = mResampler != NULL || mIsLegacyDownmix || mIsLegacyUpmix;
+
+ // do we need a staging buffer to convert for destination (we can still optimize this)?
+ // we use mBufFrameSize > 0 to indicate both frame size as well as buffer necessity
+ if (mResampler != NULL) {
+ mBufFrameSize = max(mSrcChannelCount, FCC_2)
+ * audio_bytes_per_sample(AUDIO_FORMAT_PCM_FLOAT);
+ } else if ((mIsLegacyUpmix || mIsLegacyDownmix) && mDstFormat != AUDIO_FORMAT_PCM_FLOAT) {
+ mBufFrameSize = mDstChannelCount * audio_bytes_per_sample(AUDIO_FORMAT_PCM_FLOAT);
+ } else if (mSrcChannelMask != mDstChannelMask && mDstFormat != mSrcFormat) {
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);
+ // do we need an input converter buffer provider to give us float?
+ delete mInputConverterProvider;
+ mInputConverterProvider = NULL;
+ if (mRequiresFloat && mSrcFormat != AUDIO_FORMAT_PCM_FLOAT) {
+ mInputConverterProvider = new ReformatBufferProvider(
+ audio_channel_count_from_in_mask(mSrcChannelMask),
+ mSrcFormat,
+ AUDIO_FORMAT_PCM_FLOAT,
+ 256 /* provider buffer frame count */);
+ }
+
+ // do we need a remixer to do channel mask conversion
+ if (!mIsLegacyDownmix && !mIsLegacyUpmix && mSrcChannelMask != mDstChannelMask) {
+ (void) memcpy_by_index_array_initialization_from_channel_mask(
+ mIdxAry, ARRAY_SIZE(mIdxAry), mDstChannelMask, mSrcChannelMask);
}
return NO_ERROR;
}
-void AudioFlinger::RecordThread::RecordBufferConverter::convert(
- void *dst, /*const*/ void *src, size_t frames)
+void AudioFlinger::RecordThread::RecordBufferConverter::convertNoResampler(
+ 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
+ // src is native type unless there is legacy upmix or downmix, whereupon it is float.
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.
+ // do we need to do legacy upmix and downmix?
+ if (mIsLegacyUpmix || mIsLegacyDownmix) {
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);
+ if (mIsLegacyUpmix) {
+ upmix_to_stereo_float_from_mono_float((float *)dstBuf,
+ (const float *)src, frames);
+ } else /*mIsLegacyDownmix */ {
+ downmix_to_mono_float_from_stereo_float((float *)dstBuf,
+ (const float *)src, frames);
}
- } else if (mSrcChannelCount != mDstChannelCount) {
+ if (mBuf != NULL) {
+ memcpy_by_audio_format(dst, mDstFormat, mBuf, AUDIO_FORMAT_PCM_FLOAT,
+ frames * mDstChannelCount);
+ }
+ return;
+ }
+ // do we need to do channel mask conversion?
+ if (mSrcChannelMask != mDstChannelMask) {
void *dstBuf = mBuf != NULL ? mBuf : dst;
+ memcpy_by_index_array(dstBuf, mDstChannelCount,
+ src, mSrcChannelCount, mIdxAry, audio_bytes_per_sample(mSrcFormat), frames);
+ if (dstBuf == dst) {
+ return; // format is the same
+ }
+ }
+ // convert to destination buffer
+ const void *convertBuf = mBuf != NULL ? mBuf : src;
+ memcpy_by_audio_format(dst, mDstFormat, convertBuf, mSrcFormat,
+ frames * mDstChannelCount);
+}
+
+void AudioFlinger::RecordThread::RecordBufferConverter::convertResampler(
+ void *dst, /*not-a-const*/ void *src, size_t frames)
+{
+ // src buffer format is ALWAYS float when entering this routine
+ if (mIsLegacyUpmix) {
+ ; // mono to stereo already handled by resampler
+ } else if (mIsLegacyDownmix
+ || (mSrcChannelMask == mDstChannelMask && mSrcChannelCount == 1)) {
+ // the resampler outputs stereo for mono input channel (a feature?)
+ // must convert to mono
+ downmix_to_mono_float_from_stereo_float((float *)src,
+ (const float *)src, frames);
+ } else if (mSrcChannelMask != mDstChannelMask) {
+ // convert to mono channel again for channel mask conversion (could be skipped
+ // with further optimization).
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);
+ downmix_to_mono_float_from_stereo_float((float *)src,
+ (const float *)src, frames);
}
+ // convert to destination format (in place, OK as float is larger than other types)
+ if (mDstFormat != AUDIO_FORMAT_PCM_FLOAT) {
+ memcpy_by_audio_format(src, mDstFormat, src, AUDIO_FORMAT_PCM_FLOAT,
+ frames * mSrcChannelCount);
+ }
+ // channel convert and save to dst
+ memcpy_by_index_array(dst, mDstChannelCount,
+ src, mSrcChannelCount, mIdxAry, audio_bytes_per_sample(mDstFormat), frames);
+ return;
}
- if (mSrcFormat != mDstFormat) {
- void *srcBuf = mBuf != NULL ? mBuf : src;
- memcpy_by_audio_format(dst, mDstFormat, srcBuf, mSrcFormat,
- frames * mDstChannelCount);
- }
+ // convert to destination format and save to dst
+ memcpy_by_audio_format(dst, mDstFormat, src, AUDIO_FORMAT_PCM_FLOAT,
+ frames * mDstChannelCount);
}
bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValuePair,
@@ -6421,6 +6490,10 @@ bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValueP
audio_format_t reqFormat = mFormat;
uint32_t samplingRate = mSampleRate;
audio_channel_mask_t channelMask = audio_channel_in_mask_from_count(mChannelCount);
+ // possible that we are > 2 channels, use channel index mask
+ if (channelMask == AUDIO_CHANNEL_INVALID && mChannelCount <= FCC_8) {
+ audio_channel_mask_for_index_assignment_from_count(mChannelCount);
+ }
AudioParameter param = AudioParameter(keyValuePair);
int value;
@@ -6441,7 +6514,8 @@ bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValueP
}
if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
audio_channel_mask_t mask = (audio_channel_mask_t) value;
- if (mask != AUDIO_CHANNEL_IN_MONO && mask != AUDIO_CHANNEL_IN_STEREO) {
+ if (!audio_is_input_channel(mask) ||
+ audio_channel_count_from_in_mask(mask) > FCC_8) {
status = BAD_VALUE;
} else {
channelMask = mask;
@@ -6566,10 +6640,13 @@ void AudioFlinger::RecordThread::readInputParameters_l()
mSampleRate = mInput->stream->common.get_sample_rate(&mInput->stream->common);
mChannelMask = mInput->stream->common.get_channels(&mInput->stream->common);
mChannelCount = audio_channel_count_from_in_mask(mChannelMask);
+ if (mChannelCount > FCC_8) {
+ ALOGE("HAL channel count %d > %d", mChannelCount, FCC_8);
+ }
mHALFormat = mInput->stream->common.get_format(&mInput->stream->common);
mFormat = mHALFormat;
- if (mFormat != AUDIO_FORMAT_PCM_16_BIT) {
- ALOGE("HAL format %#x not supported; must be AUDIO_FORMAT_PCM_16_BIT", mFormat);
+ if (!audio_is_linear_pcm(mFormat)) {
+ ALOGE("HAL format %#x is not linear pcm", mFormat);
}
mFrameSize = audio_stream_in_frame_size(mInput->stream);
mBufferSize = mInput->stream->common.get_buffer_size(&mInput->stream->common);
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index ed2e4a1..b7c1ed1 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1130,8 +1130,11 @@ public:
}
private:
- // internal convert function for format and channel mask.
- void convert(void *dst, /*const*/ void *src, size_t frames);
+ // format conversion when not using resampler
+ void convertNoResampler(void *dst, const void *src, size_t frames);
+
+ // format conversion when using resampler; modifies src in-place
+ void convertResampler(void *dst, /*not-a-const*/ void *src, size_t frames);
// user provided information
audio_channel_mask_t mSrcChannelMask;
@@ -1153,10 +1156,12 @@ public:
// resampler info
AudioResampler *mResampler;
- // interleaved stereo pairs of fixed-point Q4.27 or float depending on resampler
- void *mRsmpOutBuffer;
- // current allocated frame count for the above, which may be larger than needed
- size_t mRsmpOutFrameCount;
+
+ bool mIsLegacyDownmix; // legacy stereo to mono conversion needed
+ bool mIsLegacyUpmix; // legacy mono to stereo conversion needed
+ bool mRequiresFloat; // data processing requires float (e.g. resampler)
+ PassthruBufferProvider *mInputConverterProvider; // converts input to float
+ int8_t mIdxAry[sizeof(uint32_t) * 8]; // used for channel mask conversion
};
#include "RecordTracks.h"