Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d330ee46 authored by Andy Hung's avatar Andy Hung
Browse files

Add floating and multichannel record to AudioFlinger

Change-Id: Ia388fb012a0b6d81613ef87142a97d76836338f9
parent f27e2fbf
Loading
Loading
Loading
Loading
+6 −6
Original line number Diff line number Diff line
@@ -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);

+1 −1
Original line number Diff line number Diff line
@@ -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;
+144 −67
Original line number Diff line number Diff line
@@ -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;
    // 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 */);
    }
        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 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);
        }
        if (mBuf != NULL) {
            memcpy_by_audio_format(dst, mDstFormat, mBuf, AUDIO_FORMAT_PCM_FLOAT,
                    frames * mDstChannelCount);
        }
        return;
    }
    } else if (mSrcChannelCount != mDstChannelCount) {
    // do we need to do channel mask conversion?
    if (mSrcChannelMask != mDstChannelMask) {
        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);
        memcpy_by_index_array(dstBuf, mDstChannelCount,
                src, mSrcChannelCount, mIdxAry, audio_bytes_per_sample(mSrcFormat), frames);
        if (dstBuf == dst) {
            return; // format is the same
        }
    }
    if (mSrcFormat != mDstFormat) {
        void *srcBuf = mBuf != NULL ? mBuf : src;
        memcpy_by_audio_format(dst, mDstFormat, srcBuf, mSrcFormat,
    // 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) {
            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;
    }
    // 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);
+11 −6
Original line number Diff line number Diff line
@@ -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"