mirror of
				https://github.com/ioacademy-jikim/multimedia
				synced 2025-11-03 19:17:11 +00:00 
			
		
		
		
	오디오 믹서수정
This commit is contained in:
		
							parent
							
								
									2e8959101d
								
							
						
					
					
						commit
						cd8732f3b1
					
				@ -1,24 +1,18 @@
 | 
			
		||||
LOCAL_PATH:= $(call my-dir)
 | 
			
		||||
 | 
			
		||||
include $(CLEAR_VARS)
 | 
			
		||||
 | 
			
		||||
LOCAL_SRC_FILES:= \
 | 
			
		||||
	test-mixer.cpp \
 | 
			
		||||
	AudioMixer.cpp.arm \
 | 
			
		||||
LOCAL_SRC_FILES:=  test-mixer.cpp  AudioMixer.cpp.arm BufferProviders.cpp
 | 
			
		||||
 | 
			
		||||
LOCAL_C_INCLUDES := \
 | 
			
		||||
	bionic \
 | 
			
		||||
	bionic/libstdc++/include \
 | 
			
		||||
	external/stlport/stlport \
 | 
			
		||||
	$(call include-path-for, audio-effects) \
 | 
			
		||||
	$(call include-path-for, audio-utils) \
 | 
			
		||||
	frameworks/av/services/audioflinger
 | 
			
		||||
	frameworks/av/services/audioflinger \
 | 
			
		||||
	external/sonic
 | 
			
		||||
 | 
			
		||||
LOCAL_STATIC_LIBRARIES := \
 | 
			
		||||
	libsndfile
 | 
			
		||||
 | 
			
		||||
LOCAL_SHARED_LIBRARIES := \
 | 
			
		||||
	libstlport \
 | 
			
		||||
	libeffects \
 | 
			
		||||
	libnbaio \
 | 
			
		||||
	libcommon_time_client \
 | 
			
		||||
@ -27,10 +21,13 @@ LOCAL_SHARED_LIBRARIES := \
 | 
			
		||||
	libdl \
 | 
			
		||||
	libcutils \
 | 
			
		||||
	libutils \
 | 
			
		||||
	liblog
 | 
			
		||||
	liblog \
 | 
			
		||||
	libsonic
 | 
			
		||||
 | 
			
		||||
LOCAL_MODULE:= test-mixer
 | 
			
		||||
 | 
			
		||||
LOCAL_MODULE_TAGS := optional
 | 
			
		||||
 | 
			
		||||
LOCAL_CXX_STL := libc++
 | 
			
		||||
 | 
			
		||||
include $(BUILD_EXECUTABLE)
 | 
			
		||||
 | 
			
		||||
@ -39,9 +39,6 @@
 | 
			
		||||
#include <common_time/local_clock.h>
 | 
			
		||||
#include <common_time/cc_helper.h>
 | 
			
		||||
 | 
			
		||||
#include <media/EffectsFactoryApi.h>
 | 
			
		||||
#include <audio_effects/effect_downmix.h>
 | 
			
		||||
 | 
			
		||||
#include "AudioMixerOps.h"
 | 
			
		||||
#include "AudioMixer.h"
 | 
			
		||||
 | 
			
		||||
@ -69,9 +66,16 @@
 | 
			
		||||
#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Set kUseNewMixer to true to use the new mixer engine. Otherwise the
 | 
			
		||||
// original code will be used.  This is false for now.
 | 
			
		||||
static const bool kUseNewMixer = false;
 | 
			
		||||
// TODO: Move these macro/inlines to a header file.
 | 
			
		||||
template <typename T>
 | 
			
		||||
static inline
 | 
			
		||||
T max(const T& x, const T& y) {
 | 
			
		||||
    return x > y ? x : y;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set kUseNewMixer to true to use the new mixer engine always. Otherwise the
 | 
			
		||||
// original code will be used for stereo sinks, the new mixer for multichannel.
 | 
			
		||||
static const bool kUseNewMixer = true;
 | 
			
		||||
 | 
			
		||||
// Set kUseFloat to true to allow floating input into the mixer engine.
 | 
			
		||||
// If kUseNewMixer is false, this is ignored or may be overridden internally
 | 
			
		||||
@ -91,288 +95,6 @@ T min(const T& a, const T& b)
 | 
			
		||||
    return a < b ? a : b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioMixer::CopyBufferProvider::CopyBufferProvider(size_t inputFrameSize,
 | 
			
		||||
        size_t outputFrameSize, size_t bufferFrameCount) :
 | 
			
		||||
        mInputFrameSize(inputFrameSize),
 | 
			
		||||
        mOutputFrameSize(outputFrameSize),
 | 
			
		||||
        mLocalBufferFrameCount(bufferFrameCount),
 | 
			
		||||
        mLocalBufferData(NULL),
 | 
			
		||||
        mConsumed(0)
 | 
			
		||||
{
 | 
			
		||||
    ALOGV("CopyBufferProvider(%p)(%zu, %zu, %zu)", this,
 | 
			
		||||
            inputFrameSize, outputFrameSize, bufferFrameCount);
 | 
			
		||||
    LOG_ALWAYS_FATAL_IF(inputFrameSize < outputFrameSize && bufferFrameCount == 0,
 | 
			
		||||
            "Requires local buffer if inputFrameSize(%zu) < outputFrameSize(%zu)",
 | 
			
		||||
            inputFrameSize, outputFrameSize);
 | 
			
		||||
    if (mLocalBufferFrameCount) {
 | 
			
		||||
        (void)posix_memalign(&mLocalBufferData, 32, mLocalBufferFrameCount * mOutputFrameSize);
 | 
			
		||||
    }
 | 
			
		||||
    mBuffer.frameCount = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioMixer::CopyBufferProvider::~CopyBufferProvider()
 | 
			
		||||
{
 | 
			
		||||
    ALOGV("~CopyBufferProvider(%p)", this);
 | 
			
		||||
    if (mBuffer.frameCount != 0) {
 | 
			
		||||
        mTrackBufferProvider->releaseBuffer(&mBuffer);
 | 
			
		||||
    }
 | 
			
		||||
    free(mLocalBufferData);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
status_t AudioMixer::CopyBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer,
 | 
			
		||||
        int64_t pts)
 | 
			
		||||
{
 | 
			
		||||
    //ALOGV("CopyBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
 | 
			
		||||
    //        this, pBuffer, pBuffer->frameCount, pts);
 | 
			
		||||
    if (mLocalBufferFrameCount == 0) {
 | 
			
		||||
        status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts);
 | 
			
		||||
        if (res == OK) {
 | 
			
		||||
            copyFrames(pBuffer->raw, pBuffer->raw, pBuffer->frameCount);
 | 
			
		||||
        }
 | 
			
		||||
        return res;
 | 
			
		||||
    }
 | 
			
		||||
    if (mBuffer.frameCount == 0) {
 | 
			
		||||
        mBuffer.frameCount = pBuffer->frameCount;
 | 
			
		||||
        status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
 | 
			
		||||
        // At one time an upstream buffer provider had
 | 
			
		||||
        // res == OK and mBuffer.frameCount == 0, doesn't seem to happen now 7/18/2014.
 | 
			
		||||
        //
 | 
			
		||||
        // By API spec, if res != OK, then mBuffer.frameCount == 0.
 | 
			
		||||
        // but there may be improper implementations.
 | 
			
		||||
        ALOG_ASSERT(res == OK || mBuffer.frameCount == 0);
 | 
			
		||||
        if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe.
 | 
			
		||||
            pBuffer->raw = NULL;
 | 
			
		||||
            pBuffer->frameCount = 0;
 | 
			
		||||
            return res;
 | 
			
		||||
        }
 | 
			
		||||
        mConsumed = 0;
 | 
			
		||||
    }
 | 
			
		||||
    ALOG_ASSERT(mConsumed < mBuffer.frameCount);
 | 
			
		||||
    size_t count = min(mLocalBufferFrameCount, mBuffer.frameCount - mConsumed);
 | 
			
		||||
    count = min(count, pBuffer->frameCount);
 | 
			
		||||
    pBuffer->raw = mLocalBufferData;
 | 
			
		||||
    pBuffer->frameCount = count;
 | 
			
		||||
    copyFrames(pBuffer->raw, (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize,
 | 
			
		||||
            pBuffer->frameCount);
 | 
			
		||||
    return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioMixer::CopyBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer)
 | 
			
		||||
{
 | 
			
		||||
    //ALOGV("CopyBufferProvider(%p)::releaseBuffer(%p(%zu))",
 | 
			
		||||
    //        this, pBuffer, pBuffer->frameCount);
 | 
			
		||||
    if (mLocalBufferFrameCount == 0) {
 | 
			
		||||
        mTrackBufferProvider->releaseBuffer(pBuffer);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount");
 | 
			
		||||
    mConsumed += pBuffer->frameCount; // TODO: update for efficiency to reuse existing content
 | 
			
		||||
    if (mConsumed != 0 && mConsumed >= mBuffer.frameCount) {
 | 
			
		||||
        mTrackBufferProvider->releaseBuffer(&mBuffer);
 | 
			
		||||
        ALOG_ASSERT(mBuffer.frameCount == 0);
 | 
			
		||||
    }
 | 
			
		||||
    pBuffer->raw = NULL;
 | 
			
		||||
    pBuffer->frameCount = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioMixer::CopyBufferProvider::reset()
 | 
			
		||||
{
 | 
			
		||||
    if (mBuffer.frameCount != 0) {
 | 
			
		||||
        mTrackBufferProvider->releaseBuffer(&mBuffer);
 | 
			
		||||
    }
 | 
			
		||||
    mConsumed = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioMixer::DownmixerBufferProvider::DownmixerBufferProvider(
 | 
			
		||||
        audio_channel_mask_t inputChannelMask,
 | 
			
		||||
        audio_channel_mask_t outputChannelMask, audio_format_t format,
 | 
			
		||||
        uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount) :
 | 
			
		||||
        CopyBufferProvider(
 | 
			
		||||
            audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(inputChannelMask),
 | 
			
		||||
            audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(outputChannelMask),
 | 
			
		||||
            bufferFrameCount)  // set bufferFrameCount to 0 to do in-place
 | 
			
		||||
{
 | 
			
		||||
    ALOGV("DownmixerBufferProvider(%p)(%#x, %#x, %#x %u %d)",
 | 
			
		||||
            this, inputChannelMask, outputChannelMask, format,
 | 
			
		||||
            sampleRate, sessionId);
 | 
			
		||||
    if (!sIsMultichannelCapable
 | 
			
		||||
            || EffectCreate(&sDwnmFxDesc.uuid,
 | 
			
		||||
                    sessionId,
 | 
			
		||||
                    SESSION_ID_INVALID_AND_IGNORED,
 | 
			
		||||
                    &mDownmixHandle) != 0) {
 | 
			
		||||
         ALOGE("DownmixerBufferProvider() error creating downmixer effect");
 | 
			
		||||
         mDownmixHandle = NULL;
 | 
			
		||||
         return;
 | 
			
		||||
     }
 | 
			
		||||
     // channel input configuration will be overridden per-track
 | 
			
		||||
     mDownmixConfig.inputCfg.channels = inputChannelMask;   // FIXME: Should be bits
 | 
			
		||||
     mDownmixConfig.outputCfg.channels = outputChannelMask; // FIXME: should be bits
 | 
			
		||||
     mDownmixConfig.inputCfg.format = format;
 | 
			
		||||
     mDownmixConfig.outputCfg.format = format;
 | 
			
		||||
     mDownmixConfig.inputCfg.samplingRate = sampleRate;
 | 
			
		||||
     mDownmixConfig.outputCfg.samplingRate = sampleRate;
 | 
			
		||||
     mDownmixConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
 | 
			
		||||
     mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
 | 
			
		||||
     // input and output buffer provider, and frame count will not be used as the downmix effect
 | 
			
		||||
     // process() function is called directly (see DownmixerBufferProvider::getNextBuffer())
 | 
			
		||||
     mDownmixConfig.inputCfg.mask = EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS |
 | 
			
		||||
             EFFECT_CONFIG_FORMAT | EFFECT_CONFIG_ACC_MODE;
 | 
			
		||||
     mDownmixConfig.outputCfg.mask = mDownmixConfig.inputCfg.mask;
 | 
			
		||||
 | 
			
		||||
     int cmdStatus;
 | 
			
		||||
     uint32_t replySize = sizeof(int);
 | 
			
		||||
 | 
			
		||||
     // Configure downmixer
 | 
			
		||||
     status_t status = (*mDownmixHandle)->command(mDownmixHandle,
 | 
			
		||||
             EFFECT_CMD_SET_CONFIG /*cmdCode*/, sizeof(effect_config_t) /*cmdSize*/,
 | 
			
		||||
             &mDownmixConfig /*pCmdData*/,
 | 
			
		||||
             &replySize, &cmdStatus /*pReplyData*/);
 | 
			
		||||
     if (status != 0 || cmdStatus != 0) {
 | 
			
		||||
         ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while configuring downmixer",
 | 
			
		||||
                 status, cmdStatus);
 | 
			
		||||
         EffectRelease(mDownmixHandle);
 | 
			
		||||
         mDownmixHandle = NULL;
 | 
			
		||||
         return;
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
     // Enable downmixer
 | 
			
		||||
     replySize = sizeof(int);
 | 
			
		||||
     status = (*mDownmixHandle)->command(mDownmixHandle,
 | 
			
		||||
             EFFECT_CMD_ENABLE /*cmdCode*/, 0 /*cmdSize*/, NULL /*pCmdData*/,
 | 
			
		||||
             &replySize, &cmdStatus /*pReplyData*/);
 | 
			
		||||
     if (status != 0 || cmdStatus != 0) {
 | 
			
		||||
         ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while enabling downmixer",
 | 
			
		||||
                 status, cmdStatus);
 | 
			
		||||
         EffectRelease(mDownmixHandle);
 | 
			
		||||
         mDownmixHandle = NULL;
 | 
			
		||||
         return;
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
     // Set downmix type
 | 
			
		||||
     // parameter size rounded for padding on 32bit boundary
 | 
			
		||||
     const int psizePadded = ((sizeof(downmix_params_t) - 1)/sizeof(int) + 1) * sizeof(int);
 | 
			
		||||
     const int downmixParamSize =
 | 
			
		||||
             sizeof(effect_param_t) + psizePadded + sizeof(downmix_type_t);
 | 
			
		||||
     effect_param_t * const param = (effect_param_t *) malloc(downmixParamSize);
 | 
			
		||||
     param->psize = sizeof(downmix_params_t);
 | 
			
		||||
     const downmix_params_t downmixParam = DOWNMIX_PARAM_TYPE;
 | 
			
		||||
     memcpy(param->data, &downmixParam, param->psize);
 | 
			
		||||
     const downmix_type_t downmixType = DOWNMIX_TYPE_FOLD;
 | 
			
		||||
     param->vsize = sizeof(downmix_type_t);
 | 
			
		||||
     memcpy(param->data + psizePadded, &downmixType, param->vsize);
 | 
			
		||||
     replySize = sizeof(int);
 | 
			
		||||
     status = (*mDownmixHandle)->command(mDownmixHandle,
 | 
			
		||||
             EFFECT_CMD_SET_PARAM /* cmdCode */, downmixParamSize /* cmdSize */,
 | 
			
		||||
             param /*pCmdData*/, &replySize, &cmdStatus /*pReplyData*/);
 | 
			
		||||
     free(param);
 | 
			
		||||
     if (status != 0 || cmdStatus != 0) {
 | 
			
		||||
         ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while setting downmix type",
 | 
			
		||||
                 status, cmdStatus);
 | 
			
		||||
         EffectRelease(mDownmixHandle);
 | 
			
		||||
         mDownmixHandle = NULL;
 | 
			
		||||
         return;
 | 
			
		||||
     }
 | 
			
		||||
     ALOGV("DownmixerBufferProvider() downmix type set to %d", (int) downmixType);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioMixer::DownmixerBufferProvider::~DownmixerBufferProvider()
 | 
			
		||||
{
 | 
			
		||||
    ALOGV("~DownmixerBufferProvider (%p)", this);
 | 
			
		||||
    EffectRelease(mDownmixHandle);
 | 
			
		||||
    mDownmixHandle = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioMixer::DownmixerBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
 | 
			
		||||
{
 | 
			
		||||
    mDownmixConfig.inputCfg.buffer.frameCount = frames;
 | 
			
		||||
    mDownmixConfig.inputCfg.buffer.raw = const_cast<void *>(src);
 | 
			
		||||
    mDownmixConfig.outputCfg.buffer.frameCount = frames;
 | 
			
		||||
    mDownmixConfig.outputCfg.buffer.raw = dst;
 | 
			
		||||
    // may be in-place if src == dst.
 | 
			
		||||
    status_t res = (*mDownmixHandle)->process(mDownmixHandle,
 | 
			
		||||
            &mDownmixConfig.inputCfg.buffer, &mDownmixConfig.outputCfg.buffer);
 | 
			
		||||
    ALOGE_IF(res != OK, "DownmixBufferProvider error %d", res);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* call once in a pthread_once handler. */
 | 
			
		||||
/*static*/ status_t AudioMixer::DownmixerBufferProvider::init()
 | 
			
		||||
{
 | 
			
		||||
    // find multichannel downmix effect if we have to play multichannel content
 | 
			
		||||
    uint32_t numEffects = 0;
 | 
			
		||||
    int ret = EffectQueryNumberEffects(&numEffects);
 | 
			
		||||
    if (ret != 0) {
 | 
			
		||||
        ALOGE("AudioMixer() error %d querying number of effects", ret);
 | 
			
		||||
        return NO_INIT;
 | 
			
		||||
    }
 | 
			
		||||
    ALOGV("EffectQueryNumberEffects() numEffects=%d", numEffects);
 | 
			
		||||
 | 
			
		||||
    for (uint32_t i = 0 ; i < numEffects ; i++) {
 | 
			
		||||
        if (EffectQueryEffect(i, &sDwnmFxDesc) == 0) {
 | 
			
		||||
            ALOGV("effect %d is called %s", i, sDwnmFxDesc.name);
 | 
			
		||||
            if (memcmp(&sDwnmFxDesc.type, EFFECT_UIID_DOWNMIX, sizeof(effect_uuid_t)) == 0) {
 | 
			
		||||
                ALOGI("found effect \"%s\" from %s",
 | 
			
		||||
                        sDwnmFxDesc.name, sDwnmFxDesc.implementor);
 | 
			
		||||
                sIsMultichannelCapable = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    ALOGW_IF(!sIsMultichannelCapable, "unable to find downmix effect");
 | 
			
		||||
    return NO_INIT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*static*/ bool AudioMixer::DownmixerBufferProvider::sIsMultichannelCapable = false;
 | 
			
		||||
/*static*/ effect_descriptor_t AudioMixer::DownmixerBufferProvider::sDwnmFxDesc;
 | 
			
		||||
 | 
			
		||||
AudioMixer::RemixBufferProvider::RemixBufferProvider(audio_channel_mask_t inputChannelMask,
 | 
			
		||||
        audio_channel_mask_t outputChannelMask, audio_format_t format,
 | 
			
		||||
        size_t bufferFrameCount) :
 | 
			
		||||
        CopyBufferProvider(
 | 
			
		||||
                audio_bytes_per_sample(format)
 | 
			
		||||
                    * audio_channel_count_from_out_mask(inputChannelMask),
 | 
			
		||||
                audio_bytes_per_sample(format)
 | 
			
		||||
                    * audio_channel_count_from_out_mask(outputChannelMask),
 | 
			
		||||
                bufferFrameCount),
 | 
			
		||||
        mFormat(format),
 | 
			
		||||
        mSampleSize(audio_bytes_per_sample(format)),
 | 
			
		||||
        mInputChannels(audio_channel_count_from_out_mask(inputChannelMask)),
 | 
			
		||||
        mOutputChannels(audio_channel_count_from_out_mask(outputChannelMask))
 | 
			
		||||
{
 | 
			
		||||
    ALOGV("RemixBufferProvider(%p)(%#x, %#x, %#x) %zu %zu",
 | 
			
		||||
            this, format, inputChannelMask, outputChannelMask,
 | 
			
		||||
            mInputChannels, mOutputChannels);
 | 
			
		||||
    // TODO: consider channel representation in index array formulation
 | 
			
		||||
    // We ignore channel representation, and just use the bits.
 | 
			
		||||
    memcpy_by_index_array_initialization(mIdxAry, ARRAY_SIZE(mIdxAry),
 | 
			
		||||
            audio_channel_mask_get_bits(outputChannelMask),
 | 
			
		||||
            audio_channel_mask_get_bits(inputChannelMask));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioMixer::RemixBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
 | 
			
		||||
{
 | 
			
		||||
    memcpy_by_index_array(dst, mOutputChannels,
 | 
			
		||||
            src, mInputChannels, mIdxAry, mSampleSize, frames);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AudioMixer::ReformatBufferProvider::ReformatBufferProvider(int32_t channels,
 | 
			
		||||
        audio_format_t inputFormat, audio_format_t outputFormat,
 | 
			
		||||
        size_t bufferFrameCount) :
 | 
			
		||||
        CopyBufferProvider(
 | 
			
		||||
            channels * audio_bytes_per_sample(inputFormat),
 | 
			
		||||
            channels * audio_bytes_per_sample(outputFormat),
 | 
			
		||||
            bufferFrameCount),
 | 
			
		||||
        mChannels(channels),
 | 
			
		||||
        mInputFormat(inputFormat),
 | 
			
		||||
        mOutputFormat(outputFormat)
 | 
			
		||||
{
 | 
			
		||||
    ALOGV("ReformatBufferProvider(%p)(%d, %#x, %#x)", this, channels, inputFormat, outputFormat);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioMixer::ReformatBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
 | 
			
		||||
{
 | 
			
		||||
    memcpy_by_audio_format(dst, mOutputFormat, src, mInputFormat, frames * mChannels);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// Ensure mConfiguredNames bitmask is initialized properly on all architectures.
 | 
			
		||||
@ -407,6 +129,7 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate, uint32_t maxNumTr
 | 
			
		||||
        t->resampler = NULL;
 | 
			
		||||
        t->downmixerBufferProvider = NULL;
 | 
			
		||||
        t->mReformatBufferProvider = NULL;
 | 
			
		||||
        t->mTimestretchBufferProvider = NULL;
 | 
			
		||||
        t++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -419,6 +142,7 @@ AudioMixer::~AudioMixer()
 | 
			
		||||
        delete t->resampler;
 | 
			
		||||
        delete t->downmixerBufferProvider;
 | 
			
		||||
        delete t->mReformatBufferProvider;
 | 
			
		||||
        delete t->mTimestretchBufferProvider;
 | 
			
		||||
        t++;
 | 
			
		||||
    }
 | 
			
		||||
    delete [] mState.outputTemp;
 | 
			
		||||
@ -430,6 +154,10 @@ void AudioMixer::setLog(NBLog::Writer *log)
 | 
			
		||||
    mState.mLog = log;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline audio_format_t selectMixerInFormat(audio_format_t inputFormat __unused) {
 | 
			
		||||
    return kUseFloat && kUseNewMixer ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int AudioMixer::getTrackName(audio_channel_mask_t channelMask,
 | 
			
		||||
        audio_format_t format, int sessionId)
 | 
			
		||||
{
 | 
			
		||||
@ -492,24 +220,25 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask,
 | 
			
		||||
        t->mInputBufferProvider = NULL;
 | 
			
		||||
        t->mReformatBufferProvider = NULL;
 | 
			
		||||
        t->downmixerBufferProvider = NULL;
 | 
			
		||||
        t->mPostDownmixReformatBufferProvider = NULL;
 | 
			
		||||
        t->mTimestretchBufferProvider = NULL;
 | 
			
		||||
        t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
 | 
			
		||||
        t->mFormat = format;
 | 
			
		||||
        t->mMixerInFormat = kUseFloat && kUseNewMixer
 | 
			
		||||
                ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
 | 
			
		||||
        t->mMixerInFormat = selectMixerInFormat(format);
 | 
			
		||||
        t->mDownmixRequiresFormat = AUDIO_FORMAT_INVALID; // no format required
 | 
			
		||||
        t->mMixerChannelMask = audio_channel_mask_from_representation_and_bits(
 | 
			
		||||
                AUDIO_CHANNEL_REPRESENTATION_POSITION, AUDIO_CHANNEL_OUT_STEREO);
 | 
			
		||||
        t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask);
 | 
			
		||||
        t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
 | 
			
		||||
        // Check the downmixing (or upmixing) requirements.
 | 
			
		||||
        status_t status = initTrackDownmix(t, n);
 | 
			
		||||
        status_t status = t->prepareForDownmix();
 | 
			
		||||
        if (status != OK) {
 | 
			
		||||
            ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask);
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        // initTrackDownmix() may change the input format requirement.
 | 
			
		||||
        // If you desire floating point input to the mixer, it may change
 | 
			
		||||
        // to integer because the downmixer requires integer to process.
 | 
			
		||||
        // prepareForDownmix() may change mDownmixRequiresFormat
 | 
			
		||||
        ALOGVV("mMixerFormat:%#x  mMixerInFormat:%#x\n", t->mMixerFormat, t->mMixerInFormat);
 | 
			
		||||
        prepareTrackForReformat(t, n);
 | 
			
		||||
        t->prepareForReformat();
 | 
			
		||||
        mTrackNames |= 1 << n;
 | 
			
		||||
        return TRACK0 + n;
 | 
			
		||||
    }
 | 
			
		||||
@ -526,7 +255,7 @@ void AudioMixer::invalidateState(uint32_t mask)
 | 
			
		||||
 }
 | 
			
		||||
 | 
			
		||||
// Called when channel masks have changed for a track name
 | 
			
		||||
// TODO: Fix Downmixbufferprofider not to (possibly) change mixer input format,
 | 
			
		||||
// TODO: Fix DownmixerBufferProvider not to (possibly) change mixer input format,
 | 
			
		||||
// which will simplify this logic.
 | 
			
		||||
bool AudioMixer::setChannelMasks(int name,
 | 
			
		||||
        audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask) {
 | 
			
		||||
@ -551,21 +280,18 @@ bool AudioMixer::setChannelMasks(int name,
 | 
			
		||||
 | 
			
		||||
    // channel masks have changed, does this track need a downmixer?
 | 
			
		||||
    // update to try using our desired format (if we aren't already using it)
 | 
			
		||||
    const audio_format_t prevMixerInFormat = track.mMixerInFormat;
 | 
			
		||||
    track.mMixerInFormat = kUseFloat && kUseNewMixer
 | 
			
		||||
            ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
 | 
			
		||||
    const status_t status = initTrackDownmix(&mState.tracks[name], name);
 | 
			
		||||
    const audio_format_t prevDownmixerFormat = track.mDownmixRequiresFormat;
 | 
			
		||||
    const status_t status = mState.tracks[name].prepareForDownmix();
 | 
			
		||||
    ALOGE_IF(status != OK,
 | 
			
		||||
            "initTrackDownmix error %d, track channel mask %#x, mixer channel mask %#x",
 | 
			
		||||
            "prepareForDownmix error %d, track channel mask %#x, mixer channel mask %#x",
 | 
			
		||||
            status, track.channelMask, track.mMixerChannelMask);
 | 
			
		||||
 | 
			
		||||
    const bool mixerInFormatChanged = prevMixerInFormat != track.mMixerInFormat;
 | 
			
		||||
    if (mixerInFormatChanged) {
 | 
			
		||||
        prepareTrackForReformat(&track, name); // because of downmixer, track format may change!
 | 
			
		||||
    if (prevDownmixerFormat != track.mDownmixRequiresFormat) {
 | 
			
		||||
        track.prepareForReformat(); // because of downmixer, track format may change!
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (track.resampler && (mixerInFormatChanged || mixerChannelCountChanged)) {
 | 
			
		||||
        // resampler input format or channels may have changed.
 | 
			
		||||
    if (track.resampler && mixerChannelCountChanged) {
 | 
			
		||||
        // resampler channels may have changed.
 | 
			
		||||
        const uint32_t resetToSampleRate = track.sampleRate;
 | 
			
		||||
        delete track.resampler;
 | 
			
		||||
        track.resampler = NULL;
 | 
			
		||||
@ -576,99 +302,129 @@ bool AudioMixer::setChannelMasks(int name,
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
status_t AudioMixer::initTrackDownmix(track_t* pTrack, int trackName)
 | 
			
		||||
{
 | 
			
		||||
    // Only remix (upmix or downmix) if the track and mixer/device channel masks
 | 
			
		||||
    // are not the same and not handled internally, as mono -> stereo currently is.
 | 
			
		||||
    if (pTrack->channelMask != pTrack->mMixerChannelMask
 | 
			
		||||
            && !(pTrack->channelMask == AUDIO_CHANNEL_OUT_MONO
 | 
			
		||||
                    && pTrack->mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO)) {
 | 
			
		||||
        return prepareTrackForDownmix(pTrack, trackName);
 | 
			
		||||
    }
 | 
			
		||||
    // no remix necessary
 | 
			
		||||
    unprepareTrackForDownmix(pTrack, trackName);
 | 
			
		||||
    return NO_ERROR;
 | 
			
		||||
}
 | 
			
		||||
void AudioMixer::track_t::unprepareForDownmix() {
 | 
			
		||||
    ALOGV("AudioMixer::unprepareForDownmix(%p)", this);
 | 
			
		||||
 | 
			
		||||
void AudioMixer::unprepareTrackForDownmix(track_t* pTrack, int trackName __unused) {
 | 
			
		||||
    ALOGV("AudioMixer::unprepareTrackForDownmix(%d)", trackName);
 | 
			
		||||
 | 
			
		||||
    if (pTrack->downmixerBufferProvider != NULL) {
 | 
			
		||||
    mDownmixRequiresFormat = AUDIO_FORMAT_INVALID;
 | 
			
		||||
    if (downmixerBufferProvider != NULL) {
 | 
			
		||||
        // this track had previously been configured with a downmixer, delete it
 | 
			
		||||
        ALOGV(" deleting old downmixer");
 | 
			
		||||
        delete pTrack->downmixerBufferProvider;
 | 
			
		||||
        pTrack->downmixerBufferProvider = NULL;
 | 
			
		||||
        reconfigureBufferProviders(pTrack);
 | 
			
		||||
        delete downmixerBufferProvider;
 | 
			
		||||
        downmixerBufferProvider = NULL;
 | 
			
		||||
        reconfigureBufferProviders();
 | 
			
		||||
    } else {
 | 
			
		||||
        ALOGV(" nothing to do, no downmixer to delete");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
status_t AudioMixer::prepareTrackForDownmix(track_t* pTrack, int trackName)
 | 
			
		||||
status_t AudioMixer::track_t::prepareForDownmix()
 | 
			
		||||
{
 | 
			
		||||
    ALOGV("AudioMixer::prepareTrackForDownmix(%d) with mask 0x%x", trackName, pTrack->channelMask);
 | 
			
		||||
    ALOGV("AudioMixer::prepareForDownmix(%p) with mask 0x%x",
 | 
			
		||||
            this, channelMask);
 | 
			
		||||
 | 
			
		||||
    // discard the previous downmixer if there was one
 | 
			
		||||
    unprepareTrackForDownmix(pTrack, trackName);
 | 
			
		||||
    if (DownmixerBufferProvider::isMultichannelCapable()) {
 | 
			
		||||
        DownmixerBufferProvider* pDbp = new DownmixerBufferProvider(pTrack->channelMask,
 | 
			
		||||
                pTrack->mMixerChannelMask,
 | 
			
		||||
                AUDIO_FORMAT_PCM_16_BIT /* TODO: use pTrack->mMixerInFormat, now only PCM 16 */,
 | 
			
		||||
                pTrack->sampleRate, pTrack->sessionId, kCopyBufferFrameCount);
 | 
			
		||||
    unprepareForDownmix();
 | 
			
		||||
    // MONO_HACK Only remix (upmix or downmix) if the track and mixer/device channel masks
 | 
			
		||||
    // are not the same and not handled internally, as mono -> stereo currently is.
 | 
			
		||||
    if (channelMask == mMixerChannelMask
 | 
			
		||||
            || (channelMask == AUDIO_CHANNEL_OUT_MONO
 | 
			
		||||
                    && mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO)) {
 | 
			
		||||
        return NO_ERROR;
 | 
			
		||||
    }
 | 
			
		||||
    // DownmixerBufferProvider is only used for position masks.
 | 
			
		||||
    if (audio_channel_mask_get_representation(channelMask)
 | 
			
		||||
                == AUDIO_CHANNEL_REPRESENTATION_POSITION
 | 
			
		||||
            && DownmixerBufferProvider::isMultichannelCapable()) {
 | 
			
		||||
        DownmixerBufferProvider* pDbp = new DownmixerBufferProvider(channelMask,
 | 
			
		||||
                mMixerChannelMask,
 | 
			
		||||
                AUDIO_FORMAT_PCM_16_BIT /* TODO: use mMixerInFormat, now only PCM 16 */,
 | 
			
		||||
                sampleRate, sessionId, kCopyBufferFrameCount);
 | 
			
		||||
 | 
			
		||||
        if (pDbp->isValid()) { // if constructor completed properly
 | 
			
		||||
            pTrack->mMixerInFormat = AUDIO_FORMAT_PCM_16_BIT; // PCM 16 bit required for downmix
 | 
			
		||||
            pTrack->downmixerBufferProvider = pDbp;
 | 
			
		||||
            reconfigureBufferProviders(pTrack);
 | 
			
		||||
            mDownmixRequiresFormat = AUDIO_FORMAT_PCM_16_BIT; // PCM 16 bit required for downmix
 | 
			
		||||
            downmixerBufferProvider = pDbp;
 | 
			
		||||
            reconfigureBufferProviders();
 | 
			
		||||
            return NO_ERROR;
 | 
			
		||||
        }
 | 
			
		||||
        delete pDbp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Effect downmixer does not accept the channel conversion.  Let's use our remixer.
 | 
			
		||||
    RemixBufferProvider* pRbp = new RemixBufferProvider(pTrack->channelMask,
 | 
			
		||||
            pTrack->mMixerChannelMask, pTrack->mMixerInFormat, kCopyBufferFrameCount);
 | 
			
		||||
    RemixBufferProvider* pRbp = new RemixBufferProvider(channelMask,
 | 
			
		||||
            mMixerChannelMask, mMixerInFormat, kCopyBufferFrameCount);
 | 
			
		||||
    // Remix always finds a conversion whereas Downmixer effect above may fail.
 | 
			
		||||
    pTrack->downmixerBufferProvider = pRbp;
 | 
			
		||||
    reconfigureBufferProviders(pTrack);
 | 
			
		||||
    downmixerBufferProvider = pRbp;
 | 
			
		||||
    reconfigureBufferProviders();
 | 
			
		||||
    return NO_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioMixer::unprepareTrackForReformat(track_t* pTrack, int trackName __unused) {
 | 
			
		||||
    ALOGV("AudioMixer::unprepareTrackForReformat(%d)", trackName);
 | 
			
		||||
    if (pTrack->mReformatBufferProvider != NULL) {
 | 
			
		||||
        delete pTrack->mReformatBufferProvider;
 | 
			
		||||
        pTrack->mReformatBufferProvider = NULL;
 | 
			
		||||
        reconfigureBufferProviders(pTrack);
 | 
			
		||||
void AudioMixer::track_t::unprepareForReformat() {
 | 
			
		||||
    ALOGV("AudioMixer::unprepareForReformat(%p)", this);
 | 
			
		||||
    bool requiresReconfigure = false;
 | 
			
		||||
    if (mReformatBufferProvider != NULL) {
 | 
			
		||||
        delete mReformatBufferProvider;
 | 
			
		||||
        mReformatBufferProvider = NULL;
 | 
			
		||||
        requiresReconfigure = true;
 | 
			
		||||
    }
 | 
			
		||||
    if (mPostDownmixReformatBufferProvider != NULL) {
 | 
			
		||||
        delete mPostDownmixReformatBufferProvider;
 | 
			
		||||
        mPostDownmixReformatBufferProvider = NULL;
 | 
			
		||||
        requiresReconfigure = true;
 | 
			
		||||
    }
 | 
			
		||||
    if (requiresReconfigure) {
 | 
			
		||||
        reconfigureBufferProviders();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
status_t AudioMixer::prepareTrackForReformat(track_t* pTrack, int trackName)
 | 
			
		||||
status_t AudioMixer::track_t::prepareForReformat()
 | 
			
		||||
{
 | 
			
		||||
    ALOGV("AudioMixer::prepareTrackForReformat(%d) with format %#x", trackName, pTrack->mFormat);
 | 
			
		||||
    // discard the previous reformatter if there was one
 | 
			
		||||
    unprepareTrackForReformat(pTrack, trackName);
 | 
			
		||||
    // only configure reformatter if needed
 | 
			
		||||
    if (pTrack->mFormat != pTrack->mMixerInFormat) {
 | 
			
		||||
        pTrack->mReformatBufferProvider = new ReformatBufferProvider(
 | 
			
		||||
                audio_channel_count_from_out_mask(pTrack->channelMask),
 | 
			
		||||
                pTrack->mFormat, pTrack->mMixerInFormat,
 | 
			
		||||
    ALOGV("AudioMixer::prepareForReformat(%p) with format %#x", this, mFormat);
 | 
			
		||||
    // discard previous reformatters
 | 
			
		||||
    unprepareForReformat();
 | 
			
		||||
    // only configure reformatters as needed
 | 
			
		||||
    const audio_format_t targetFormat = mDownmixRequiresFormat != AUDIO_FORMAT_INVALID
 | 
			
		||||
            ? mDownmixRequiresFormat : mMixerInFormat;
 | 
			
		||||
    bool requiresReconfigure = false;
 | 
			
		||||
    if (mFormat != targetFormat) {
 | 
			
		||||
        mReformatBufferProvider = new ReformatBufferProvider(
 | 
			
		||||
                audio_channel_count_from_out_mask(channelMask),
 | 
			
		||||
                mFormat,
 | 
			
		||||
                targetFormat,
 | 
			
		||||
                kCopyBufferFrameCount);
 | 
			
		||||
        reconfigureBufferProviders(pTrack);
 | 
			
		||||
        requiresReconfigure = true;
 | 
			
		||||
    }
 | 
			
		||||
    if (targetFormat != mMixerInFormat) {
 | 
			
		||||
        mPostDownmixReformatBufferProvider = new ReformatBufferProvider(
 | 
			
		||||
                audio_channel_count_from_out_mask(mMixerChannelMask),
 | 
			
		||||
                targetFormat,
 | 
			
		||||
                mMixerInFormat,
 | 
			
		||||
                kCopyBufferFrameCount);
 | 
			
		||||
        requiresReconfigure = true;
 | 
			
		||||
    }
 | 
			
		||||
    if (requiresReconfigure) {
 | 
			
		||||
        reconfigureBufferProviders();
 | 
			
		||||
    }
 | 
			
		||||
    return NO_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AudioMixer::reconfigureBufferProviders(track_t* pTrack)
 | 
			
		||||
void AudioMixer::track_t::reconfigureBufferProviders()
 | 
			
		||||
{
 | 
			
		||||
    pTrack->bufferProvider = pTrack->mInputBufferProvider;
 | 
			
		||||
    if (pTrack->mReformatBufferProvider) {
 | 
			
		||||
        pTrack->mReformatBufferProvider->setBufferProvider(pTrack->bufferProvider);
 | 
			
		||||
        pTrack->bufferProvider = pTrack->mReformatBufferProvider;
 | 
			
		||||
    bufferProvider = mInputBufferProvider;
 | 
			
		||||
    if (mReformatBufferProvider) {
 | 
			
		||||
        mReformatBufferProvider->setBufferProvider(bufferProvider);
 | 
			
		||||
        bufferProvider = mReformatBufferProvider;
 | 
			
		||||
    }
 | 
			
		||||
    if (pTrack->downmixerBufferProvider) {
 | 
			
		||||
        pTrack->downmixerBufferProvider->setBufferProvider(pTrack->bufferProvider);
 | 
			
		||||
        pTrack->bufferProvider = pTrack->downmixerBufferProvider;
 | 
			
		||||
    if (downmixerBufferProvider) {
 | 
			
		||||
        downmixerBufferProvider->setBufferProvider(bufferProvider);
 | 
			
		||||
        bufferProvider = downmixerBufferProvider;
 | 
			
		||||
    }
 | 
			
		||||
    if (mPostDownmixReformatBufferProvider) {
 | 
			
		||||
        mPostDownmixReformatBufferProvider->setBufferProvider(bufferProvider);
 | 
			
		||||
        bufferProvider = mPostDownmixReformatBufferProvider;
 | 
			
		||||
    }
 | 
			
		||||
    if (mTimestretchBufferProvider) {
 | 
			
		||||
        mTimestretchBufferProvider->setBufferProvider(bufferProvider);
 | 
			
		||||
        bufferProvider = mTimestretchBufferProvider;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -687,10 +443,12 @@ void AudioMixer::deleteTrackName(int name)
 | 
			
		||||
    delete track.resampler;
 | 
			
		||||
    track.resampler = NULL;
 | 
			
		||||
    // delete the downmixer
 | 
			
		||||
    unprepareTrackForDownmix(&mState.tracks[name], name);
 | 
			
		||||
    mState.tracks[name].unprepareForDownmix();
 | 
			
		||||
    // delete the reformatter
 | 
			
		||||
    unprepareTrackForReformat(&mState.tracks[name], name);
 | 
			
		||||
 | 
			
		||||
    mState.tracks[name].unprepareForReformat();
 | 
			
		||||
    // delete the timestretch provider
 | 
			
		||||
    delete track.mTimestretchBufferProvider;
 | 
			
		||||
    track.mTimestretchBufferProvider = NULL;
 | 
			
		||||
    mTrackNames &= ~(1<<name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -748,41 +506,99 @@ void AudioMixer::disable(int name)
 | 
			
		||||
static inline bool setVolumeRampVariables(float newVolume, int32_t ramp,
 | 
			
		||||
        int16_t *pIntSetVolume, int32_t *pIntPrevVolume, int32_t *pIntVolumeInc,
 | 
			
		||||
        float *pSetVolume, float *pPrevVolume, float *pVolumeInc) {
 | 
			
		||||
    // check floating point volume to see if it is identical to the previously
 | 
			
		||||
    // set volume.
 | 
			
		||||
    // We do not use a tolerance here (and reject changes too small)
 | 
			
		||||
    // as it may be confusing to use a different value than the one set.
 | 
			
		||||
    // If the resulting volume is too small to ramp, it is a direct set of the volume.
 | 
			
		||||
    if (newVolume == *pSetVolume) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    /* set the floating point volume variables */
 | 
			
		||||
    if (ramp != 0) {
 | 
			
		||||
        *pVolumeInc = (newVolume - *pSetVolume) / ramp;
 | 
			
		||||
        *pPrevVolume = *pSetVolume;
 | 
			
		||||
    if (newVolume < 0) {
 | 
			
		||||
        newVolume = 0; // should not have negative volumes
 | 
			
		||||
    } else {
 | 
			
		||||
        *pVolumeInc = 0;
 | 
			
		||||
        *pPrevVolume = newVolume;
 | 
			
		||||
        switch (fpclassify(newVolume)) {
 | 
			
		||||
        case FP_SUBNORMAL:
 | 
			
		||||
        case FP_NAN:
 | 
			
		||||
            newVolume = 0;
 | 
			
		||||
            break;
 | 
			
		||||
        case FP_ZERO:
 | 
			
		||||
            break; // zero volume is fine
 | 
			
		||||
        case FP_INFINITE:
 | 
			
		||||
            // Infinite volume could be handled consistently since
 | 
			
		||||
            // floating point math saturates at infinities,
 | 
			
		||||
            // but we limit volume to unity gain float.
 | 
			
		||||
            // ramp = 0; break;
 | 
			
		||||
            //
 | 
			
		||||
            newVolume = AudioMixer::UNITY_GAIN_FLOAT;
 | 
			
		||||
            break;
 | 
			
		||||
        case FP_NORMAL:
 | 
			
		||||
        default:
 | 
			
		||||
            // Floating point does not have problems with overflow wrap
 | 
			
		||||
            // that integer has.  However, we limit the volume to
 | 
			
		||||
            // unity gain here.
 | 
			
		||||
            // TODO: Revisit the volume limitation and perhaps parameterize.
 | 
			
		||||
            if (newVolume > AudioMixer::UNITY_GAIN_FLOAT) {
 | 
			
		||||
                newVolume = AudioMixer::UNITY_GAIN_FLOAT;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    *pSetVolume = newVolume;
 | 
			
		||||
 | 
			
		||||
    /* set the legacy integer volume variables */
 | 
			
		||||
    int32_t intVolume = newVolume * AudioMixer::UNITY_GAIN_INT;
 | 
			
		||||
    if (intVolume > AudioMixer::UNITY_GAIN_INT) {
 | 
			
		||||
        intVolume = AudioMixer::UNITY_GAIN_INT;
 | 
			
		||||
    } else if (intVolume < 0) {
 | 
			
		||||
        ALOGE("negative volume %.7g", newVolume);
 | 
			
		||||
        intVolume = 0; // should never happen, but for safety check.
 | 
			
		||||
    // set floating point volume ramp
 | 
			
		||||
    if (ramp != 0) {
 | 
			
		||||
        // when the ramp completes, *pPrevVolume is set to *pSetVolume, so there
 | 
			
		||||
        // is no computational mismatch; hence equality is checked here.
 | 
			
		||||
        ALOGD_IF(*pPrevVolume != *pSetVolume, "previous float ramp hasn't finished,"
 | 
			
		||||
                " prev:%f  set_to:%f", *pPrevVolume, *pSetVolume);
 | 
			
		||||
        const float inc = (newVolume - *pPrevVolume) / ramp; // could be inf, nan, subnormal
 | 
			
		||||
        const float maxv = max(newVolume, *pPrevVolume); // could be inf, cannot be nan, subnormal
 | 
			
		||||
 | 
			
		||||
        if (isnormal(inc) // inc must be a normal number (no subnormals, infinite, nan)
 | 
			
		||||
                && maxv + inc != maxv) { // inc must make forward progress
 | 
			
		||||
            *pVolumeInc = inc;
 | 
			
		||||
            // ramp is set now.
 | 
			
		||||
            // Note: if newVolume is 0, then near the end of the ramp,
 | 
			
		||||
            // it may be possible that the ramped volume may be subnormal or
 | 
			
		||||
            // temporarily negative by a small amount or subnormal due to floating
 | 
			
		||||
            // point inaccuracies.
 | 
			
		||||
        } else {
 | 
			
		||||
            ramp = 0; // ramp not allowed
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (intVolume == *pIntSetVolume) {
 | 
			
		||||
        *pIntVolumeInc = 0;
 | 
			
		||||
        /* TODO: integer/float workaround: ignore floating volume ramp */
 | 
			
		||||
 | 
			
		||||
    // compute and check integer volume, no need to check negative values
 | 
			
		||||
    // The integer volume is limited to "unity_gain" to avoid wrapping and other
 | 
			
		||||
    // audio artifacts, so it never reaches the range limit of U4.28.
 | 
			
		||||
    // We safely use signed 16 and 32 bit integers here.
 | 
			
		||||
    const float scaledVolume = newVolume * AudioMixer::UNITY_GAIN_INT; // not neg, subnormal, nan
 | 
			
		||||
    const int32_t intVolume = (scaledVolume >= (float)AudioMixer::UNITY_GAIN_INT) ?
 | 
			
		||||
            AudioMixer::UNITY_GAIN_INT : (int32_t)scaledVolume;
 | 
			
		||||
 | 
			
		||||
    // set integer volume ramp
 | 
			
		||||
    if (ramp != 0) {
 | 
			
		||||
        // integer volume is U4.12 (to use 16 bit multiplies), but ramping uses U4.28.
 | 
			
		||||
        // when the ramp completes, *pIntPrevVolume is set to *pIntSetVolume << 16, so there
 | 
			
		||||
        // is no computational mismatch; hence equality is checked here.
 | 
			
		||||
        ALOGD_IF(*pIntPrevVolume != *pIntSetVolume << 16, "previous int ramp hasn't finished,"
 | 
			
		||||
                " prev:%d  set_to:%d", *pIntPrevVolume, *pIntSetVolume << 16);
 | 
			
		||||
        const int32_t inc = ((intVolume << 16) - *pIntPrevVolume) / ramp;
 | 
			
		||||
 | 
			
		||||
        if (inc != 0) { // inc must make forward progress
 | 
			
		||||
            *pIntVolumeInc = inc;
 | 
			
		||||
        } else {
 | 
			
		||||
            ramp = 0; // ramp not allowed
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // if no ramp, or ramp not allowed, then clear float and integer increments
 | 
			
		||||
    if (ramp == 0) {
 | 
			
		||||
        *pVolumeInc = 0;
 | 
			
		||||
        *pPrevVolume = newVolume;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    if (ramp != 0) {
 | 
			
		||||
        *pIntVolumeInc = ((intVolume - *pIntSetVolume) << 16) / ramp;
 | 
			
		||||
        *pIntPrevVolume = (*pIntVolumeInc == 0 ? intVolume : *pIntSetVolume) << 16;
 | 
			
		||||
    } else {
 | 
			
		||||
        *pIntVolumeInc = 0;
 | 
			
		||||
        *pIntPrevVolume = intVolume << 16;
 | 
			
		||||
    }
 | 
			
		||||
    *pSetVolume = newVolume;
 | 
			
		||||
    *pIntSetVolume = intVolume;
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
@ -828,7 +644,7 @@ void AudioMixer::setParameter(int name, int target, int param, void *value)
 | 
			
		||||
                ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format);
 | 
			
		||||
                track.mFormat = format;
 | 
			
		||||
                ALOGV("setParameter(TRACK, FORMAT, %#x)", format);
 | 
			
		||||
                prepareTrackForReformat(&track, name);
 | 
			
		||||
                track.prepareForReformat();
 | 
			
		||||
                invalidateState(1 << name);
 | 
			
		||||
            }
 | 
			
		||||
            } break;
 | 
			
		||||
@ -912,6 +728,28 @@ void AudioMixer::setParameter(int name, int target, int param, void *value)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
        case TIMESTRETCH:
 | 
			
		||||
            switch (param) {
 | 
			
		||||
            case PLAYBACK_RATE: {
 | 
			
		||||
                const AudioPlaybackRate *playbackRate =
 | 
			
		||||
                        reinterpret_cast<AudioPlaybackRate*>(value);
 | 
			
		||||
                ALOGW_IF(!isAudioPlaybackRateValid(*playbackRate),
 | 
			
		||||
                        "bad parameters speed %f, pitch %f",playbackRate->mSpeed,
 | 
			
		||||
                        playbackRate->mPitch);
 | 
			
		||||
                if (track.setPlaybackRate(*playbackRate)) {
 | 
			
		||||
                    ALOGV("setParameter(TIMESTRETCH, PLAYBACK_RATE, STRETCH_MODE, FALLBACK_MODE "
 | 
			
		||||
                            "%f %f %d %d",
 | 
			
		||||
                            playbackRate->mSpeed,
 | 
			
		||||
                            playbackRate->mPitch,
 | 
			
		||||
                            playbackRate->mStretchMode,
 | 
			
		||||
                            playbackRate->mFallbackMode);
 | 
			
		||||
                    // invalidateState(1 << name);
 | 
			
		||||
                }
 | 
			
		||||
            } break;
 | 
			
		||||
            default:
 | 
			
		||||
                LOG_ALWAYS_FATAL("setParameter timestretch: bad param %d", param);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        LOG_ALWAYS_FATAL("setParameter: bad target %d", target);
 | 
			
		||||
@ -931,11 +769,10 @@ bool AudioMixer::track_t::setResampler(uint32_t trackSampleRate, uint32_t devSam
 | 
			
		||||
                // FIXME this is flawed for dynamic sample rates, as we choose the resampler
 | 
			
		||||
                // quality level based on the initial ratio, but that could change later.
 | 
			
		||||
                // Should have a way to distinguish tracks with static ratios vs. dynamic ratios.
 | 
			
		||||
                if (!((trackSampleRate == 44100 && devSampleRate == 48000) ||
 | 
			
		||||
                      (trackSampleRate == 48000 && devSampleRate == 44100))) {
 | 
			
		||||
                    quality = AudioResampler::DYN_LOW_QUALITY;
 | 
			
		||||
                } else {
 | 
			
		||||
                if (isMusicRate(trackSampleRate)) {
 | 
			
		||||
                    quality = AudioResampler::DEFAULT_QUALITY;
 | 
			
		||||
                } else {
 | 
			
		||||
                    quality = AudioResampler::DYN_LOW_QUALITY;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer
 | 
			
		||||
@ -957,6 +794,30 @@ bool AudioMixer::track_t::setResampler(uint32_t trackSampleRate, uint32_t devSam
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool AudioMixer::track_t::setPlaybackRate(const AudioPlaybackRate &playbackRate)
 | 
			
		||||
{
 | 
			
		||||
    if ((mTimestretchBufferProvider == NULL &&
 | 
			
		||||
            fabs(playbackRate.mSpeed - mPlaybackRate.mSpeed) < AUDIO_TIMESTRETCH_SPEED_MIN_DELTA &&
 | 
			
		||||
            fabs(playbackRate.mPitch - mPlaybackRate.mPitch) < AUDIO_TIMESTRETCH_PITCH_MIN_DELTA) ||
 | 
			
		||||
            isAudioPlaybackRateEqual(playbackRate, mPlaybackRate)) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    mPlaybackRate = playbackRate;
 | 
			
		||||
    if (mTimestretchBufferProvider == NULL) {
 | 
			
		||||
        // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer
 | 
			
		||||
        // but if none exists, it is the channel count (1 for mono).
 | 
			
		||||
        const int timestretchChannelCount = downmixerBufferProvider != NULL
 | 
			
		||||
                ? mMixerChannelCount : channelCount;
 | 
			
		||||
        mTimestretchBufferProvider = new TimestretchBufferProvider(timestretchChannelCount,
 | 
			
		||||
                mMixerInFormat, sampleRate, playbackRate);
 | 
			
		||||
        reconfigureBufferProviders();
 | 
			
		||||
    } else {
 | 
			
		||||
        reinterpret_cast<TimestretchBufferProvider*>(mTimestretchBufferProvider)
 | 
			
		||||
                ->setPlaybackRate(playbackRate);
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Checks to see if the volume ramp has completed and clears the increment
 | 
			
		||||
 * variables appropriately.
 | 
			
		||||
 *
 | 
			
		||||
@ -974,7 +835,8 @@ inline void AudioMixer::track_t::adjustVolumeRamp(bool aux, bool useFloat)
 | 
			
		||||
{
 | 
			
		||||
    if (useFloat) {
 | 
			
		||||
        for (uint32_t i = 0; i < MAX_NUM_VOLUMES; i++) {
 | 
			
		||||
            if (mVolumeInc[i] != 0 && fabs(mVolume[i] - mPrevVolume[i]) <= fabs(mVolumeInc[i])) {
 | 
			
		||||
            if ((mVolumeInc[i] > 0 && mPrevVolume[i] + mVolumeInc[i] >= mVolume[i]) ||
 | 
			
		||||
                     (mVolumeInc[i] < 0 && mPrevVolume[i] + mVolumeInc[i] <= mVolume[i])) {
 | 
			
		||||
                volumeInc[i] = 0;
 | 
			
		||||
                prevVolume[i] = volume[i] << 16;
 | 
			
		||||
                mVolumeInc[i] = 0.;
 | 
			
		||||
@ -1032,10 +894,15 @@ void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider
 | 
			
		||||
    if (mState.tracks[name].mReformatBufferProvider != NULL) {
 | 
			
		||||
        mState.tracks[name].mReformatBufferProvider->reset();
 | 
			
		||||
    } else if (mState.tracks[name].downmixerBufferProvider != NULL) {
 | 
			
		||||
        mState.tracks[name].downmixerBufferProvider->reset();
 | 
			
		||||
    } else if (mState.tracks[name].mPostDownmixReformatBufferProvider != NULL) {
 | 
			
		||||
        mState.tracks[name].mPostDownmixReformatBufferProvider->reset();
 | 
			
		||||
    } else if (mState.tracks[name].mTimestretchBufferProvider != NULL) {
 | 
			
		||||
        mState.tracks[name].mTimestretchBufferProvider->reset();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mState.tracks[name].mInputBufferProvider = bufferProvider;
 | 
			
		||||
    reconfigureBufferProviders(&mState.tracks[name]);
 | 
			
		||||
    mState.tracks[name].reconfigureBufferProviders();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1114,7 +981,8 @@ void AudioMixer::process__validate(state_t* state, int64_t pts)
 | 
			
		||||
            } else {
 | 
			
		||||
                if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
 | 
			
		||||
                    t.hook = getTrackHook(
 | 
			
		||||
                            t.mMixerChannelCount == 2 // TODO: MONO_HACK.
 | 
			
		||||
                            (t.mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO  // TODO: MONO_HACK
 | 
			
		||||
                                    && t.channelMask == AUDIO_CHANNEL_OUT_MONO)
 | 
			
		||||
                                ? TRACKTYPE_NORESAMPLEMONO : TRACKTYPE_NORESAMPLE,
 | 
			
		||||
                            t.mMixerChannelCount,
 | 
			
		||||
                            t.mMixerInFormat, t.mMixerFormat);
 | 
			
		||||
@ -2236,4 +2104,4 @@ AudioMixer::process_hook_t AudioMixer::getProcessHook(int processType, uint32_t
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ----------------------------------------------------------------------------
 | 
			
		||||
}; // namespace android
 | 
			
		||||
} // namespace android
 | 
			
		||||
 | 
			
		||||
@ -21,14 +21,16 @@
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
 | 
			
		||||
#include <hardware/audio_effect.h>
 | 
			
		||||
#include <media/AudioBufferProvider.h>
 | 
			
		||||
#include <media/AudioResamplerPublic.h>
 | 
			
		||||
#include <media/nbaio/NBLog.h>
 | 
			
		||||
#include <system/audio.h>
 | 
			
		||||
#include <utils/Compat.h>
 | 
			
		||||
#include <utils/threads.h>
 | 
			
		||||
 | 
			
		||||
#include <media/AudioBufferProvider.h>
 | 
			
		||||
#include "AudioResampler.h"
 | 
			
		||||
 | 
			
		||||
#include <hardware/audio_effect.h>
 | 
			
		||||
#include <system/audio.h>
 | 
			
		||||
#include <media/nbaio/NBLog.h>
 | 
			
		||||
#include "BufferProviders.h"
 | 
			
		||||
 | 
			
		||||
// FIXME This is actually unity gain, which might not be max in future, expressed in U.12
 | 
			
		||||
#define MAX_GAIN_INT AudioMixer::UNITY_GAIN_INT
 | 
			
		||||
@ -58,7 +60,7 @@ public:
 | 
			
		||||
    static const uint32_t MAX_NUM_CHANNELS_TO_DOWNMIX = AUDIO_CHANNEL_COUNT_MAX;
 | 
			
		||||
 | 
			
		||||
    static const uint16_t UNITY_GAIN_INT = 0x1000;
 | 
			
		||||
    static const float    UNITY_GAIN_FLOAT = 1.0f;
 | 
			
		||||
    static const CONSTEXPR float UNITY_GAIN_FLOAT = 1.0f;
 | 
			
		||||
 | 
			
		||||
    enum { // names
 | 
			
		||||
 | 
			
		||||
@ -72,6 +74,7 @@ public:
 | 
			
		||||
        RESAMPLE        = 0x3001,
 | 
			
		||||
        RAMP_VOLUME     = 0x3002, // ramp to new volume
 | 
			
		||||
        VOLUME          = 0x3003, // don't ramp
 | 
			
		||||
        TIMESTRETCH     = 0x3004,
 | 
			
		||||
 | 
			
		||||
        // set Parameter names
 | 
			
		||||
        // for target TRACK
 | 
			
		||||
@ -99,6 +102,9 @@ public:
 | 
			
		||||
        VOLUME0         = 0x4200,
 | 
			
		||||
        VOLUME1         = 0x4201,
 | 
			
		||||
        AUXLEVEL        = 0x4210,
 | 
			
		||||
        // for target TIMESTRETCH
 | 
			
		||||
        PLAYBACK_RATE   = 0x4300, // Configure timestretch on this track name;
 | 
			
		||||
                                  // parameter 'value' is a pointer to the new playback rate.
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -127,10 +133,16 @@ public:
 | 
			
		||||
    size_t      getUnreleasedFrames(int name) const;
 | 
			
		||||
 | 
			
		||||
    static inline bool isValidPcmTrackFormat(audio_format_t format) {
 | 
			
		||||
        return format == AUDIO_FORMAT_PCM_16_BIT ||
 | 
			
		||||
                format == AUDIO_FORMAT_PCM_24_BIT_PACKED ||
 | 
			
		||||
                format == AUDIO_FORMAT_PCM_32_BIT ||
 | 
			
		||||
                format == AUDIO_FORMAT_PCM_FLOAT;
 | 
			
		||||
        switch (format) {
 | 
			
		||||
        case AUDIO_FORMAT_PCM_8_BIT:
 | 
			
		||||
        case AUDIO_FORMAT_PCM_16_BIT:
 | 
			
		||||
        case AUDIO_FORMAT_PCM_24_BIT_PACKED:
 | 
			
		||||
        case AUDIO_FORMAT_PCM_32_BIT:
 | 
			
		||||
        case AUDIO_FORMAT_PCM_FLOAT:
 | 
			
		||||
            return true;
 | 
			
		||||
        default:
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
@ -153,7 +165,6 @@ private:
 | 
			
		||||
 | 
			
		||||
    struct state_t;
 | 
			
		||||
    struct track_t;
 | 
			
		||||
    class CopyBufferProvider;
 | 
			
		||||
 | 
			
		||||
    typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp,
 | 
			
		||||
                           int32_t* aux);
 | 
			
		||||
@ -205,17 +216,38 @@ private:
 | 
			
		||||
        int32_t*           auxBuffer;
 | 
			
		||||
 | 
			
		||||
        // 16-byte boundary
 | 
			
		||||
 | 
			
		||||
        /* Buffer providers are constructed to translate the track input data as needed.
 | 
			
		||||
         *
 | 
			
		||||
         * TODO: perhaps make a single PlaybackConverterProvider class to move
 | 
			
		||||
         * all pre-mixer track buffer conversions outside the AudioMixer class.
 | 
			
		||||
         *
 | 
			
		||||
         * 1) mInputBufferProvider: The AudioTrack buffer provider.
 | 
			
		||||
         * 2) mReformatBufferProvider: If not NULL, performs the audio reformat to
 | 
			
		||||
         *    match either mMixerInFormat or mDownmixRequiresFormat, if the downmixer
 | 
			
		||||
         *    requires reformat. For example, it may convert floating point input to
 | 
			
		||||
         *    PCM_16_bit if that's required by the downmixer.
 | 
			
		||||
         * 3) downmixerBufferProvider: If not NULL, performs the channel remixing to match
 | 
			
		||||
         *    the number of channels required by the mixer sink.
 | 
			
		||||
         * 4) mPostDownmixReformatBufferProvider: If not NULL, performs reformatting from
 | 
			
		||||
         *    the downmixer requirements to the mixer engine input requirements.
 | 
			
		||||
         * 5) mTimestretchBufferProvider: Adds timestretching for playback rate
 | 
			
		||||
         */
 | 
			
		||||
        AudioBufferProvider*     mInputBufferProvider;    // externally provided buffer provider.
 | 
			
		||||
        CopyBufferProvider*      mReformatBufferProvider; // provider wrapper for reformatting.
 | 
			
		||||
        CopyBufferProvider*      downmixerBufferProvider; // wrapper for channel conversion.
 | 
			
		||||
        PassthruBufferProvider*  mReformatBufferProvider; // provider wrapper for reformatting.
 | 
			
		||||
        PassthruBufferProvider*  downmixerBufferProvider; // wrapper for channel conversion.
 | 
			
		||||
        PassthruBufferProvider*  mPostDownmixReformatBufferProvider;
 | 
			
		||||
        PassthruBufferProvider*  mTimestretchBufferProvider;
 | 
			
		||||
 | 
			
		||||
        int32_t     sessionId;
 | 
			
		||||
 | 
			
		||||
        // 16-byte boundary
 | 
			
		||||
        audio_format_t mMixerFormat;     // output mix format: AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
 | 
			
		||||
        audio_format_t mFormat;          // input track format
 | 
			
		||||
        audio_format_t mMixerInFormat;   // mix internal format AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
 | 
			
		||||
                                         // each track must be converted to this format.
 | 
			
		||||
        audio_format_t mDownmixRequiresFormat;  // required downmixer format
 | 
			
		||||
                                                // AUDIO_FORMAT_PCM_16_BIT if 16 bit necessary
 | 
			
		||||
                                                // AUDIO_FORMAT_INVALID if no required format
 | 
			
		||||
 | 
			
		||||
        float          mVolume[MAX_NUM_VOLUMES];     // floating point set volume
 | 
			
		||||
        float          mPrevVolume[MAX_NUM_VOLUMES]; // floating point previous volume
 | 
			
		||||
@ -225,10 +257,11 @@ private:
 | 
			
		||||
        float          mPrevAuxLevel;                 // floating point prev aux level
 | 
			
		||||
        float          mAuxInc;                       // floating point aux increment
 | 
			
		||||
 | 
			
		||||
        // 16-byte boundary
 | 
			
		||||
        audio_channel_mask_t mMixerChannelMask;
 | 
			
		||||
        uint32_t             mMixerChannelCount;
 | 
			
		||||
 | 
			
		||||
        AudioPlaybackRate    mPlaybackRate;
 | 
			
		||||
 | 
			
		||||
        bool        needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; }
 | 
			
		||||
        bool        setResampler(uint32_t trackSampleRate, uint32_t devSampleRate);
 | 
			
		||||
        bool        doesResample() const { return resampler != NULL; }
 | 
			
		||||
@ -236,6 +269,13 @@ private:
 | 
			
		||||
        void        adjustVolumeRamp(bool aux, bool useFloat = false);
 | 
			
		||||
        size_t      getUnreleasedFrames() const { return resampler != NULL ?
 | 
			
		||||
                                                    resampler->getUnreleasedFrames() : 0; };
 | 
			
		||||
 | 
			
		||||
        status_t    prepareForDownmix();
 | 
			
		||||
        void        unprepareForDownmix();
 | 
			
		||||
        status_t    prepareForReformat();
 | 
			
		||||
        void        unprepareForReformat();
 | 
			
		||||
        bool        setPlaybackRate(const AudioPlaybackRate &playbackRate);
 | 
			
		||||
        void        reconfigureBufferProviders();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    typedef void (*process_hook_t)(state_t* state, int64_t pts);
 | 
			
		||||
@ -254,112 +294,6 @@ private:
 | 
			
		||||
        track_t         tracks[MAX_NUM_TRACKS] __attribute__((aligned(32)));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Base AudioBufferProvider class used for DownMixerBufferProvider, RemixBufferProvider,
 | 
			
		||||
    // and ReformatBufferProvider.
 | 
			
		||||
    // It handles a private buffer for use in converting format or channel masks from the
 | 
			
		||||
    // input data to a form acceptable by the mixer.
 | 
			
		||||
    // TODO: Make a ResamplerBufferProvider when integers are entirely removed from the
 | 
			
		||||
    // processing pipeline.
 | 
			
		||||
    class CopyBufferProvider : public AudioBufferProvider {
 | 
			
		||||
    public:
 | 
			
		||||
        // Use a private buffer of bufferFrameCount frames (each frame is outputFrameSize bytes).
 | 
			
		||||
        // If bufferFrameCount is 0, no private buffer is created and in-place modification of
 | 
			
		||||
        // the upstream buffer provider's buffers is performed by copyFrames().
 | 
			
		||||
        CopyBufferProvider(size_t inputFrameSize, size_t outputFrameSize,
 | 
			
		||||
                size_t bufferFrameCount);
 | 
			
		||||
        virtual ~CopyBufferProvider();
 | 
			
		||||
 | 
			
		||||
        // Overrides AudioBufferProvider methods
 | 
			
		||||
        virtual status_t getNextBuffer(Buffer* buffer, int64_t pts);
 | 
			
		||||
        virtual void releaseBuffer(Buffer* buffer);
 | 
			
		||||
 | 
			
		||||
        // Other public methods
 | 
			
		||||
 | 
			
		||||
        // call this to release the buffer to the upstream provider.
 | 
			
		||||
        // treat it as an audio discontinuity for future samples.
 | 
			
		||||
        virtual void reset();
 | 
			
		||||
 | 
			
		||||
        // this function should be supplied by the derived class.  It converts
 | 
			
		||||
        // #frames in the *src pointer to the *dst pointer.  It is public because
 | 
			
		||||
        // some providers will allow this to work on arbitrary buffers outside
 | 
			
		||||
        // of the internal buffers.
 | 
			
		||||
        virtual void copyFrames(void *dst, const void *src, size_t frames) = 0;
 | 
			
		||||
 | 
			
		||||
        // set the upstream buffer provider. Consider calling "reset" before this function.
 | 
			
		||||
        void setBufferProvider(AudioBufferProvider *p) {
 | 
			
		||||
            mTrackBufferProvider = p;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    protected:
 | 
			
		||||
        AudioBufferProvider* mTrackBufferProvider;
 | 
			
		||||
        const size_t         mInputFrameSize;
 | 
			
		||||
        const size_t         mOutputFrameSize;
 | 
			
		||||
    private:
 | 
			
		||||
        AudioBufferProvider::Buffer mBuffer;
 | 
			
		||||
        const size_t         mLocalBufferFrameCount;
 | 
			
		||||
        void*                mLocalBufferData;
 | 
			
		||||
        size_t               mConsumed;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // DownmixerBufferProvider wraps a track AudioBufferProvider to provide
 | 
			
		||||
    // position dependent downmixing by an Audio Effect.
 | 
			
		||||
    class DownmixerBufferProvider : public CopyBufferProvider {
 | 
			
		||||
    public:
 | 
			
		||||
        DownmixerBufferProvider(audio_channel_mask_t inputChannelMask,
 | 
			
		||||
                audio_channel_mask_t outputChannelMask, audio_format_t format,
 | 
			
		||||
                uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount);
 | 
			
		||||
        virtual ~DownmixerBufferProvider();
 | 
			
		||||
        virtual void copyFrames(void *dst, const void *src, size_t frames);
 | 
			
		||||
        bool isValid() const { return mDownmixHandle != NULL; }
 | 
			
		||||
 | 
			
		||||
        static status_t init();
 | 
			
		||||
        static bool isMultichannelCapable() { return sIsMultichannelCapable; }
 | 
			
		||||
 | 
			
		||||
    protected:
 | 
			
		||||
        effect_handle_t    mDownmixHandle;
 | 
			
		||||
        effect_config_t    mDownmixConfig;
 | 
			
		||||
 | 
			
		||||
        // effect descriptor for the downmixer used by the mixer
 | 
			
		||||
        static effect_descriptor_t sDwnmFxDesc;
 | 
			
		||||
        // indicates whether a downmix effect has been found and is usable by this mixer
 | 
			
		||||
        static bool                sIsMultichannelCapable;
 | 
			
		||||
        // FIXME: should we allow effects outside of the framework?
 | 
			
		||||
        // We need to here. A special ioId that must be <= -2 so it does not map to a session.
 | 
			
		||||
        static const int32_t SESSION_ID_INVALID_AND_IGNORED = -2;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // RemixBufferProvider wraps a track AudioBufferProvider to perform an
 | 
			
		||||
    // upmix or downmix to the proper channel count and mask.
 | 
			
		||||
    class RemixBufferProvider : public CopyBufferProvider {
 | 
			
		||||
    public:
 | 
			
		||||
        RemixBufferProvider(audio_channel_mask_t inputChannelMask,
 | 
			
		||||
                audio_channel_mask_t outputChannelMask, audio_format_t format,
 | 
			
		||||
                size_t bufferFrameCount);
 | 
			
		||||
        virtual void copyFrames(void *dst, const void *src, size_t frames);
 | 
			
		||||
 | 
			
		||||
    protected:
 | 
			
		||||
        const audio_format_t mFormat;
 | 
			
		||||
        const size_t         mSampleSize;
 | 
			
		||||
        const size_t         mInputChannels;
 | 
			
		||||
        const size_t         mOutputChannels;
 | 
			
		||||
        int8_t               mIdxAry[sizeof(uint32_t)*8]; // 32 bits => channel indices
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // ReformatBufferProvider wraps a track AudioBufferProvider to convert the input data
 | 
			
		||||
    // to an acceptable mixer input format type.
 | 
			
		||||
    class ReformatBufferProvider : public CopyBufferProvider {
 | 
			
		||||
    public:
 | 
			
		||||
        ReformatBufferProvider(int32_t channels,
 | 
			
		||||
                audio_format_t inputFormat, audio_format_t outputFormat,
 | 
			
		||||
                size_t bufferFrameCount);
 | 
			
		||||
        virtual void copyFrames(void *dst, const void *src, size_t frames);
 | 
			
		||||
 | 
			
		||||
    protected:
 | 
			
		||||
        const int32_t        mChannels;
 | 
			
		||||
        const audio_format_t mInputFormat;
 | 
			
		||||
        const audio_format_t mOutputFormat;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc.
 | 
			
		||||
    uint32_t        mTrackNames;
 | 
			
		||||
 | 
			
		||||
@ -382,14 +316,6 @@ private:
 | 
			
		||||
    bool setChannelMasks(int name,
 | 
			
		||||
            audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask);
 | 
			
		||||
 | 
			
		||||
    // TODO: remove unused trackName/trackNum from functions below.
 | 
			
		||||
    static status_t initTrackDownmix(track_t* pTrack, int trackName);
 | 
			
		||||
    static status_t prepareTrackForDownmix(track_t* pTrack, int trackNum);
 | 
			
		||||
    static void unprepareTrackForDownmix(track_t* pTrack, int trackName);
 | 
			
		||||
    static status_t prepareTrackForReformat(track_t* pTrack, int trackNum);
 | 
			
		||||
    static void unprepareTrackForReformat(track_t* pTrack, int trackName);
 | 
			
		||||
    static void reconfigureBufferProviders(track_t* pTrack);
 | 
			
		||||
 | 
			
		||||
    static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp,
 | 
			
		||||
            int32_t* aux);
 | 
			
		||||
    static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
 | 
			
		||||
@ -465,6 +391,6 @@ private:
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// ----------------------------------------------------------------------------
 | 
			
		||||
}; // namespace android
 | 
			
		||||
} // namespace android
 | 
			
		||||
 | 
			
		||||
#endif // ANDROID_AUDIO_MIXER_H
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										542
									
								
								02_day/test-mixer/BufferProviders.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										542
									
								
								02_day/test-mixer/BufferProviders.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,542 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2015 The Android Open Source Project
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define LOG_TAG "BufferProvider"
 | 
			
		||||
//#define LOG_NDEBUG 0
 | 
			
		||||
 | 
			
		||||
#include <audio_effects/effect_downmix.h>
 | 
			
		||||
#include <audio_utils/primitives.h>
 | 
			
		||||
#include <audio_utils/format.h>
 | 
			
		||||
#include <media/AudioResamplerPublic.h>
 | 
			
		||||
#include <media/EffectsFactoryApi.h>
 | 
			
		||||
 | 
			
		||||
#include <utils/Log.h>
 | 
			
		||||
 | 
			
		||||
#include "Configuration.h"
 | 
			
		||||
#include "BufferProviders.h"
 | 
			
		||||
 | 
			
		||||
#ifndef ARRAY_SIZE
 | 
			
		||||
#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace android {
 | 
			
		||||
 | 
			
		||||
// ----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
static inline T min(const T& a, const T& b)
 | 
			
		||||
{
 | 
			
		||||
    return a < b ? a : b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CopyBufferProvider::CopyBufferProvider(size_t inputFrameSize,
 | 
			
		||||
        size_t outputFrameSize, size_t bufferFrameCount) :
 | 
			
		||||
        mInputFrameSize(inputFrameSize),
 | 
			
		||||
        mOutputFrameSize(outputFrameSize),
 | 
			
		||||
        mLocalBufferFrameCount(bufferFrameCount),
 | 
			
		||||
        mLocalBufferData(NULL),
 | 
			
		||||
        mConsumed(0)
 | 
			
		||||
{
 | 
			
		||||
    ALOGV("CopyBufferProvider(%p)(%zu, %zu, %zu)", this,
 | 
			
		||||
            inputFrameSize, outputFrameSize, bufferFrameCount);
 | 
			
		||||
    LOG_ALWAYS_FATAL_IF(inputFrameSize < outputFrameSize && bufferFrameCount == 0,
 | 
			
		||||
            "Requires local buffer if inputFrameSize(%zu) < outputFrameSize(%zu)",
 | 
			
		||||
            inputFrameSize, outputFrameSize);
 | 
			
		||||
    if (mLocalBufferFrameCount) {
 | 
			
		||||
        (void)posix_memalign(&mLocalBufferData, 32, mLocalBufferFrameCount * mOutputFrameSize);
 | 
			
		||||
    }
 | 
			
		||||
    mBuffer.frameCount = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CopyBufferProvider::~CopyBufferProvider()
 | 
			
		||||
{
 | 
			
		||||
    ALOGV("~CopyBufferProvider(%p)", this);
 | 
			
		||||
    if (mBuffer.frameCount != 0) {
 | 
			
		||||
        mTrackBufferProvider->releaseBuffer(&mBuffer);
 | 
			
		||||
    }
 | 
			
		||||
    free(mLocalBufferData);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
status_t CopyBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer,
 | 
			
		||||
        int64_t pts)
 | 
			
		||||
{
 | 
			
		||||
    //ALOGV("CopyBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
 | 
			
		||||
    //        this, pBuffer, pBuffer->frameCount, pts);
 | 
			
		||||
    if (mLocalBufferFrameCount == 0) {
 | 
			
		||||
        status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts);
 | 
			
		||||
        if (res == OK) {
 | 
			
		||||
            copyFrames(pBuffer->raw, pBuffer->raw, pBuffer->frameCount);
 | 
			
		||||
        }
 | 
			
		||||
        return res;
 | 
			
		||||
    }
 | 
			
		||||
    if (mBuffer.frameCount == 0) {
 | 
			
		||||
        mBuffer.frameCount = pBuffer->frameCount;
 | 
			
		||||
        status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
 | 
			
		||||
        // At one time an upstream buffer provider had
 | 
			
		||||
        // res == OK and mBuffer.frameCount == 0, doesn't seem to happen now 7/18/2014.
 | 
			
		||||
        //
 | 
			
		||||
        // By API spec, if res != OK, then mBuffer.frameCount == 0.
 | 
			
		||||
        // but there may be improper implementations.
 | 
			
		||||
        ALOG_ASSERT(res == OK || mBuffer.frameCount == 0);
 | 
			
		||||
        if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe.
 | 
			
		||||
            pBuffer->raw = NULL;
 | 
			
		||||
            pBuffer->frameCount = 0;
 | 
			
		||||
            return res;
 | 
			
		||||
        }
 | 
			
		||||
        mConsumed = 0;
 | 
			
		||||
    }
 | 
			
		||||
    ALOG_ASSERT(mConsumed < mBuffer.frameCount);
 | 
			
		||||
    size_t count = min(mLocalBufferFrameCount, mBuffer.frameCount - mConsumed);
 | 
			
		||||
    count = min(count, pBuffer->frameCount);
 | 
			
		||||
    pBuffer->raw = mLocalBufferData;
 | 
			
		||||
    pBuffer->frameCount = count;
 | 
			
		||||
    copyFrames(pBuffer->raw, (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize,
 | 
			
		||||
            pBuffer->frameCount);
 | 
			
		||||
    return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CopyBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer)
 | 
			
		||||
{
 | 
			
		||||
    //ALOGV("CopyBufferProvider(%p)::releaseBuffer(%p(%zu))",
 | 
			
		||||
    //        this, pBuffer, pBuffer->frameCount);
 | 
			
		||||
    if (mLocalBufferFrameCount == 0) {
 | 
			
		||||
        mTrackBufferProvider->releaseBuffer(pBuffer);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount");
 | 
			
		||||
    mConsumed += pBuffer->frameCount; // TODO: update for efficiency to reuse existing content
 | 
			
		||||
    if (mConsumed != 0 && mConsumed >= mBuffer.frameCount) {
 | 
			
		||||
        mTrackBufferProvider->releaseBuffer(&mBuffer);
 | 
			
		||||
        ALOG_ASSERT(mBuffer.frameCount == 0);
 | 
			
		||||
    }
 | 
			
		||||
    pBuffer->raw = NULL;
 | 
			
		||||
    pBuffer->frameCount = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CopyBufferProvider::reset()
 | 
			
		||||
{
 | 
			
		||||
    if (mBuffer.frameCount != 0) {
 | 
			
		||||
        mTrackBufferProvider->releaseBuffer(&mBuffer);
 | 
			
		||||
    }
 | 
			
		||||
    mConsumed = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DownmixerBufferProvider::DownmixerBufferProvider(
 | 
			
		||||
        audio_channel_mask_t inputChannelMask,
 | 
			
		||||
        audio_channel_mask_t outputChannelMask, audio_format_t format,
 | 
			
		||||
        uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount) :
 | 
			
		||||
        CopyBufferProvider(
 | 
			
		||||
            audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(inputChannelMask),
 | 
			
		||||
            audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(outputChannelMask),
 | 
			
		||||
            bufferFrameCount)  // set bufferFrameCount to 0 to do in-place
 | 
			
		||||
{
 | 
			
		||||
    ALOGV("DownmixerBufferProvider(%p)(%#x, %#x, %#x %u %d)",
 | 
			
		||||
            this, inputChannelMask, outputChannelMask, format,
 | 
			
		||||
            sampleRate, sessionId);
 | 
			
		||||
    if (!sIsMultichannelCapable
 | 
			
		||||
            || EffectCreate(&sDwnmFxDesc.uuid,
 | 
			
		||||
                    sessionId,
 | 
			
		||||
                    SESSION_ID_INVALID_AND_IGNORED,
 | 
			
		||||
                    &mDownmixHandle) != 0) {
 | 
			
		||||
         ALOGE("DownmixerBufferProvider() error creating downmixer effect");
 | 
			
		||||
         mDownmixHandle = NULL;
 | 
			
		||||
         return;
 | 
			
		||||
     }
 | 
			
		||||
     // channel input configuration will be overridden per-track
 | 
			
		||||
     mDownmixConfig.inputCfg.channels = inputChannelMask;   // FIXME: Should be bits
 | 
			
		||||
     mDownmixConfig.outputCfg.channels = outputChannelMask; // FIXME: should be bits
 | 
			
		||||
     mDownmixConfig.inputCfg.format = format;
 | 
			
		||||
     mDownmixConfig.outputCfg.format = format;
 | 
			
		||||
     mDownmixConfig.inputCfg.samplingRate = sampleRate;
 | 
			
		||||
     mDownmixConfig.outputCfg.samplingRate = sampleRate;
 | 
			
		||||
     mDownmixConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
 | 
			
		||||
     mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
 | 
			
		||||
     // input and output buffer provider, and frame count will not be used as the downmix effect
 | 
			
		||||
     // process() function is called directly (see DownmixerBufferProvider::getNextBuffer())
 | 
			
		||||
     mDownmixConfig.inputCfg.mask = EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS |
 | 
			
		||||
             EFFECT_CONFIG_FORMAT | EFFECT_CONFIG_ACC_MODE;
 | 
			
		||||
     mDownmixConfig.outputCfg.mask = mDownmixConfig.inputCfg.mask;
 | 
			
		||||
 | 
			
		||||
     int cmdStatus;
 | 
			
		||||
     uint32_t replySize = sizeof(int);
 | 
			
		||||
 | 
			
		||||
     // Configure downmixer
 | 
			
		||||
     status_t status = (*mDownmixHandle)->command(mDownmixHandle,
 | 
			
		||||
             EFFECT_CMD_SET_CONFIG /*cmdCode*/, sizeof(effect_config_t) /*cmdSize*/,
 | 
			
		||||
             &mDownmixConfig /*pCmdData*/,
 | 
			
		||||
             &replySize, &cmdStatus /*pReplyData*/);
 | 
			
		||||
     if (status != 0 || cmdStatus != 0) {
 | 
			
		||||
         ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while configuring downmixer",
 | 
			
		||||
                 status, cmdStatus);
 | 
			
		||||
         EffectRelease(mDownmixHandle);
 | 
			
		||||
         mDownmixHandle = NULL;
 | 
			
		||||
         return;
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
     // Enable downmixer
 | 
			
		||||
     replySize = sizeof(int);
 | 
			
		||||
     status = (*mDownmixHandle)->command(mDownmixHandle,
 | 
			
		||||
             EFFECT_CMD_ENABLE /*cmdCode*/, 0 /*cmdSize*/, NULL /*pCmdData*/,
 | 
			
		||||
             &replySize, &cmdStatus /*pReplyData*/);
 | 
			
		||||
     if (status != 0 || cmdStatus != 0) {
 | 
			
		||||
         ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while enabling downmixer",
 | 
			
		||||
                 status, cmdStatus);
 | 
			
		||||
         EffectRelease(mDownmixHandle);
 | 
			
		||||
         mDownmixHandle = NULL;
 | 
			
		||||
         return;
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
     // Set downmix type
 | 
			
		||||
     // parameter size rounded for padding on 32bit boundary
 | 
			
		||||
     const int psizePadded = ((sizeof(downmix_params_t) - 1)/sizeof(int) + 1) * sizeof(int);
 | 
			
		||||
     const int downmixParamSize =
 | 
			
		||||
             sizeof(effect_param_t) + psizePadded + sizeof(downmix_type_t);
 | 
			
		||||
     effect_param_t * const param = (effect_param_t *) malloc(downmixParamSize);
 | 
			
		||||
     param->psize = sizeof(downmix_params_t);
 | 
			
		||||
     const downmix_params_t downmixParam = DOWNMIX_PARAM_TYPE;
 | 
			
		||||
     memcpy(param->data, &downmixParam, param->psize);
 | 
			
		||||
     const downmix_type_t downmixType = DOWNMIX_TYPE_FOLD;
 | 
			
		||||
     param->vsize = sizeof(downmix_type_t);
 | 
			
		||||
     memcpy(param->data + psizePadded, &downmixType, param->vsize);
 | 
			
		||||
     replySize = sizeof(int);
 | 
			
		||||
     status = (*mDownmixHandle)->command(mDownmixHandle,
 | 
			
		||||
             EFFECT_CMD_SET_PARAM /* cmdCode */, downmixParamSize /* cmdSize */,
 | 
			
		||||
             param /*pCmdData*/, &replySize, &cmdStatus /*pReplyData*/);
 | 
			
		||||
     free(param);
 | 
			
		||||
     if (status != 0 || cmdStatus != 0) {
 | 
			
		||||
         ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while setting downmix type",
 | 
			
		||||
                 status, cmdStatus);
 | 
			
		||||
         EffectRelease(mDownmixHandle);
 | 
			
		||||
         mDownmixHandle = NULL;
 | 
			
		||||
         return;
 | 
			
		||||
     }
 | 
			
		||||
     ALOGV("DownmixerBufferProvider() downmix type set to %d", (int) downmixType);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DownmixerBufferProvider::~DownmixerBufferProvider()
 | 
			
		||||
{
 | 
			
		||||
    ALOGV("~DownmixerBufferProvider (%p)", this);
 | 
			
		||||
    EffectRelease(mDownmixHandle);
 | 
			
		||||
    mDownmixHandle = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DownmixerBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
 | 
			
		||||
{
 | 
			
		||||
    mDownmixConfig.inputCfg.buffer.frameCount = frames;
 | 
			
		||||
    mDownmixConfig.inputCfg.buffer.raw = const_cast<void *>(src);
 | 
			
		||||
    mDownmixConfig.outputCfg.buffer.frameCount = frames;
 | 
			
		||||
    mDownmixConfig.outputCfg.buffer.raw = dst;
 | 
			
		||||
    // may be in-place if src == dst.
 | 
			
		||||
    status_t res = (*mDownmixHandle)->process(mDownmixHandle,
 | 
			
		||||
            &mDownmixConfig.inputCfg.buffer, &mDownmixConfig.outputCfg.buffer);
 | 
			
		||||
    ALOGE_IF(res != OK, "DownmixBufferProvider error %d", res);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* call once in a pthread_once handler. */
 | 
			
		||||
/*static*/ status_t DownmixerBufferProvider::init()
 | 
			
		||||
{
 | 
			
		||||
    // find multichannel downmix effect if we have to play multichannel content
 | 
			
		||||
    uint32_t numEffects = 0;
 | 
			
		||||
    int ret = EffectQueryNumberEffects(&numEffects);
 | 
			
		||||
    if (ret != 0) {
 | 
			
		||||
        ALOGE("AudioMixer() error %d querying number of effects", ret);
 | 
			
		||||
        return NO_INIT;
 | 
			
		||||
    }
 | 
			
		||||
    ALOGV("EffectQueryNumberEffects() numEffects=%d", numEffects);
 | 
			
		||||
 | 
			
		||||
    for (uint32_t i = 0 ; i < numEffects ; i++) {
 | 
			
		||||
        if (EffectQueryEffect(i, &sDwnmFxDesc) == 0) {
 | 
			
		||||
            ALOGV("effect %d is called %s", i, sDwnmFxDesc.name);
 | 
			
		||||
            if (memcmp(&sDwnmFxDesc.type, EFFECT_UIID_DOWNMIX, sizeof(effect_uuid_t)) == 0) {
 | 
			
		||||
                ALOGI("found effect \"%s\" from %s",
 | 
			
		||||
                        sDwnmFxDesc.name, sDwnmFxDesc.implementor);
 | 
			
		||||
                sIsMultichannelCapable = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    ALOGW_IF(!sIsMultichannelCapable, "unable to find downmix effect");
 | 
			
		||||
    return NO_INIT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*static*/ bool DownmixerBufferProvider::sIsMultichannelCapable = false;
 | 
			
		||||
/*static*/ effect_descriptor_t DownmixerBufferProvider::sDwnmFxDesc;
 | 
			
		||||
 | 
			
		||||
RemixBufferProvider::RemixBufferProvider(audio_channel_mask_t inputChannelMask,
 | 
			
		||||
        audio_channel_mask_t outputChannelMask, audio_format_t format,
 | 
			
		||||
        size_t bufferFrameCount) :
 | 
			
		||||
        CopyBufferProvider(
 | 
			
		||||
                audio_bytes_per_sample(format)
 | 
			
		||||
                    * audio_channel_count_from_out_mask(inputChannelMask),
 | 
			
		||||
                audio_bytes_per_sample(format)
 | 
			
		||||
                    * audio_channel_count_from_out_mask(outputChannelMask),
 | 
			
		||||
                bufferFrameCount),
 | 
			
		||||
        mFormat(format),
 | 
			
		||||
        mSampleSize(audio_bytes_per_sample(format)),
 | 
			
		||||
        mInputChannels(audio_channel_count_from_out_mask(inputChannelMask)),
 | 
			
		||||
        mOutputChannels(audio_channel_count_from_out_mask(outputChannelMask))
 | 
			
		||||
{
 | 
			
		||||
    ALOGV("RemixBufferProvider(%p)(%#x, %#x, %#x) %zu %zu",
 | 
			
		||||
            this, format, inputChannelMask, outputChannelMask,
 | 
			
		||||
            mInputChannels, mOutputChannels);
 | 
			
		||||
    (void) memcpy_by_index_array_initialization_from_channel_mask(
 | 
			
		||||
            mIdxAry, ARRAY_SIZE(mIdxAry), outputChannelMask, inputChannelMask);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RemixBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
 | 
			
		||||
{
 | 
			
		||||
    memcpy_by_index_array(dst, mOutputChannels,
 | 
			
		||||
            src, mInputChannels, mIdxAry, mSampleSize, frames);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ReformatBufferProvider::ReformatBufferProvider(int32_t channelCount,
 | 
			
		||||
        audio_format_t inputFormat, audio_format_t outputFormat,
 | 
			
		||||
        size_t bufferFrameCount) :
 | 
			
		||||
        CopyBufferProvider(
 | 
			
		||||
                channelCount * audio_bytes_per_sample(inputFormat),
 | 
			
		||||
                channelCount * audio_bytes_per_sample(outputFormat),
 | 
			
		||||
                bufferFrameCount),
 | 
			
		||||
        mChannelCount(channelCount),
 | 
			
		||||
        mInputFormat(inputFormat),
 | 
			
		||||
        mOutputFormat(outputFormat)
 | 
			
		||||
{
 | 
			
		||||
    ALOGV("ReformatBufferProvider(%p)(%u, %#x, %#x)",
 | 
			
		||||
            this, channelCount, inputFormat, outputFormat);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ReformatBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
 | 
			
		||||
{
 | 
			
		||||
    memcpy_by_audio_format(dst, mOutputFormat, src, mInputFormat, frames * mChannelCount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TimestretchBufferProvider::TimestretchBufferProvider(int32_t channelCount,
 | 
			
		||||
        audio_format_t format, uint32_t sampleRate, const AudioPlaybackRate &playbackRate) :
 | 
			
		||||
        mChannelCount(channelCount),
 | 
			
		||||
        mFormat(format),
 | 
			
		||||
        mSampleRate(sampleRate),
 | 
			
		||||
        mFrameSize(channelCount * audio_bytes_per_sample(format)),
 | 
			
		||||
        mLocalBufferFrameCount(0),
 | 
			
		||||
        mLocalBufferData(NULL),
 | 
			
		||||
        mRemaining(0),
 | 
			
		||||
        mSonicStream(sonicCreateStream(sampleRate, mChannelCount)),
 | 
			
		||||
        mFallbackFailErrorShown(false),
 | 
			
		||||
        mAudioPlaybackRateValid(false)
 | 
			
		||||
{
 | 
			
		||||
    LOG_ALWAYS_FATAL_IF(mSonicStream == NULL,
 | 
			
		||||
            "TimestretchBufferProvider can't allocate Sonic stream");
 | 
			
		||||
 | 
			
		||||
    setPlaybackRate(playbackRate);
 | 
			
		||||
    ALOGV("TimestretchBufferProvider(%p)(%u, %#x, %u %f %f %d %d)",
 | 
			
		||||
            this, channelCount, format, sampleRate, playbackRate.mSpeed,
 | 
			
		||||
            playbackRate.mPitch, playbackRate.mStretchMode, playbackRate.mFallbackMode);
 | 
			
		||||
    mBuffer.frameCount = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TimestretchBufferProvider::~TimestretchBufferProvider()
 | 
			
		||||
{
 | 
			
		||||
    ALOGV("~TimestretchBufferProvider(%p)", this);
 | 
			
		||||
    sonicDestroyStream(mSonicStream);
 | 
			
		||||
    if (mBuffer.frameCount != 0) {
 | 
			
		||||
        mTrackBufferProvider->releaseBuffer(&mBuffer);
 | 
			
		||||
    }
 | 
			
		||||
    free(mLocalBufferData);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
status_t TimestretchBufferProvider::getNextBuffer(
 | 
			
		||||
        AudioBufferProvider::Buffer *pBuffer, int64_t pts)
 | 
			
		||||
{
 | 
			
		||||
    ALOGV("TimestretchBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
 | 
			
		||||
            this, pBuffer, pBuffer->frameCount, pts);
 | 
			
		||||
 | 
			
		||||
    // BYPASS
 | 
			
		||||
    //return mTrackBufferProvider->getNextBuffer(pBuffer, pts);
 | 
			
		||||
 | 
			
		||||
    // check if previously processed data is sufficient.
 | 
			
		||||
    if (pBuffer->frameCount <= mRemaining) {
 | 
			
		||||
        ALOGV("previous sufficient");
 | 
			
		||||
        pBuffer->raw = mLocalBufferData;
 | 
			
		||||
        return OK;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // do we need to resize our buffer?
 | 
			
		||||
    if (pBuffer->frameCount > mLocalBufferFrameCount) {
 | 
			
		||||
        void *newmem;
 | 
			
		||||
        if (posix_memalign(&newmem, 32, pBuffer->frameCount * mFrameSize) == OK) {
 | 
			
		||||
            if (mRemaining != 0) {
 | 
			
		||||
                memcpy(newmem, mLocalBufferData, mRemaining * mFrameSize);
 | 
			
		||||
            }
 | 
			
		||||
            free(mLocalBufferData);
 | 
			
		||||
            mLocalBufferData = newmem;
 | 
			
		||||
            mLocalBufferFrameCount = pBuffer->frameCount;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // need to fetch more data
 | 
			
		||||
    const size_t outputDesired = pBuffer->frameCount - mRemaining;
 | 
			
		||||
    size_t dstAvailable;
 | 
			
		||||
    do {
 | 
			
		||||
        mBuffer.frameCount = mPlaybackRate.mSpeed == AUDIO_TIMESTRETCH_SPEED_NORMAL
 | 
			
		||||
                ? outputDesired : outputDesired * mPlaybackRate.mSpeed + 1;
 | 
			
		||||
 | 
			
		||||
        status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
 | 
			
		||||
 | 
			
		||||
        ALOG_ASSERT(res == OK || mBuffer.frameCount == 0);
 | 
			
		||||
        if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe.
 | 
			
		||||
            ALOGV("upstream provider cannot provide data");
 | 
			
		||||
            if (mRemaining == 0) {
 | 
			
		||||
                pBuffer->raw = NULL;
 | 
			
		||||
                pBuffer->frameCount = 0;
 | 
			
		||||
                return res;
 | 
			
		||||
            } else { // return partial count
 | 
			
		||||
                pBuffer->raw = mLocalBufferData;
 | 
			
		||||
                pBuffer->frameCount = mRemaining;
 | 
			
		||||
                return OK;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // time-stretch the data
 | 
			
		||||
        dstAvailable = min(mLocalBufferFrameCount - mRemaining, outputDesired);
 | 
			
		||||
        size_t srcAvailable = mBuffer.frameCount;
 | 
			
		||||
        processFrames((uint8_t*)mLocalBufferData + mRemaining * mFrameSize, &dstAvailable,
 | 
			
		||||
                mBuffer.raw, &srcAvailable);
 | 
			
		||||
 | 
			
		||||
        // release all data consumed
 | 
			
		||||
        mBuffer.frameCount = srcAvailable;
 | 
			
		||||
        mTrackBufferProvider->releaseBuffer(&mBuffer);
 | 
			
		||||
    } while (dstAvailable == 0); // try until we get output data or upstream provider fails.
 | 
			
		||||
 | 
			
		||||
    // update buffer vars with the actual data processed and return with buffer
 | 
			
		||||
    mRemaining += dstAvailable;
 | 
			
		||||
 | 
			
		||||
    pBuffer->raw = mLocalBufferData;
 | 
			
		||||
    pBuffer->frameCount = mRemaining;
 | 
			
		||||
 | 
			
		||||
    return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TimestretchBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer)
 | 
			
		||||
{
 | 
			
		||||
    ALOGV("TimestretchBufferProvider(%p)::releaseBuffer(%p (%zu))",
 | 
			
		||||
       this, pBuffer, pBuffer->frameCount);
 | 
			
		||||
 | 
			
		||||
    // BYPASS
 | 
			
		||||
    //return mTrackBufferProvider->releaseBuffer(pBuffer);
 | 
			
		||||
 | 
			
		||||
    // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount");
 | 
			
		||||
    if (pBuffer->frameCount < mRemaining) {
 | 
			
		||||
        memcpy(mLocalBufferData,
 | 
			
		||||
                (uint8_t*)mLocalBufferData + pBuffer->frameCount * mFrameSize,
 | 
			
		||||
                (mRemaining - pBuffer->frameCount) * mFrameSize);
 | 
			
		||||
        mRemaining -= pBuffer->frameCount;
 | 
			
		||||
    } else if (pBuffer->frameCount == mRemaining) {
 | 
			
		||||
        mRemaining = 0;
 | 
			
		||||
    } else {
 | 
			
		||||
        LOG_ALWAYS_FATAL("Releasing more frames(%zu) than available(%zu)",
 | 
			
		||||
                pBuffer->frameCount, mRemaining);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pBuffer->raw = NULL;
 | 
			
		||||
    pBuffer->frameCount = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TimestretchBufferProvider::reset()
 | 
			
		||||
{
 | 
			
		||||
    mRemaining = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
status_t TimestretchBufferProvider::setPlaybackRate(const AudioPlaybackRate &playbackRate)
 | 
			
		||||
{
 | 
			
		||||
    mPlaybackRate = playbackRate;
 | 
			
		||||
    mFallbackFailErrorShown = false;
 | 
			
		||||
    sonicSetSpeed(mSonicStream, mPlaybackRate.mSpeed);
 | 
			
		||||
    //TODO: pitch is ignored for now
 | 
			
		||||
    //TODO: optimize: if parameters are the same, don't do any extra computation.
 | 
			
		||||
 | 
			
		||||
    mAudioPlaybackRateValid = isAudioPlaybackRateValid(mPlaybackRate);
 | 
			
		||||
    return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TimestretchBufferProvider::processFrames(void *dstBuffer, size_t *dstFrames,
 | 
			
		||||
        const void *srcBuffer, size_t *srcFrames)
 | 
			
		||||
{
 | 
			
		||||
    ALOGV("processFrames(%zu %zu)  remaining(%zu)", *dstFrames, *srcFrames, mRemaining);
 | 
			
		||||
    // Note dstFrames is the required number of frames.
 | 
			
		||||
 | 
			
		||||
    // Ensure consumption from src is as expected.
 | 
			
		||||
    //TODO: add logic to track "very accurate" consumption related to speed, original sampling
 | 
			
		||||
    //rate, actual frames processed.
 | 
			
		||||
    const size_t targetSrc = *dstFrames * mPlaybackRate.mSpeed;
 | 
			
		||||
    if (*srcFrames < targetSrc) { // limit dst frames to that possible
 | 
			
		||||
        *dstFrames = *srcFrames / mPlaybackRate.mSpeed;
 | 
			
		||||
    } else if (*srcFrames > targetSrc + 1) {
 | 
			
		||||
        *srcFrames = targetSrc + 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!mAudioPlaybackRateValid) {
 | 
			
		||||
        //fallback mode
 | 
			
		||||
        if (*dstFrames > 0) {
 | 
			
		||||
            switch(mPlaybackRate.mFallbackMode) {
 | 
			
		||||
            case AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT:
 | 
			
		||||
                if (*dstFrames <= *srcFrames) {
 | 
			
		||||
                      size_t copySize = mFrameSize * *dstFrames;
 | 
			
		||||
                      memcpy(dstBuffer, srcBuffer, copySize);
 | 
			
		||||
                  } else {
 | 
			
		||||
                      // cyclically repeat the source.
 | 
			
		||||
                      for (size_t count = 0; count < *dstFrames; count += *srcFrames) {
 | 
			
		||||
                          size_t remaining = min(*srcFrames, *dstFrames - count);
 | 
			
		||||
                          memcpy((uint8_t*)dstBuffer + mFrameSize * count,
 | 
			
		||||
                                  srcBuffer, mFrameSize * remaining);
 | 
			
		||||
                      }
 | 
			
		||||
                  }
 | 
			
		||||
                break;
 | 
			
		||||
            case AUDIO_TIMESTRETCH_FALLBACK_DEFAULT:
 | 
			
		||||
            case AUDIO_TIMESTRETCH_FALLBACK_MUTE:
 | 
			
		||||
                memset(dstBuffer,0, mFrameSize * *dstFrames);
 | 
			
		||||
                break;
 | 
			
		||||
            case AUDIO_TIMESTRETCH_FALLBACK_FAIL:
 | 
			
		||||
            default:
 | 
			
		||||
                if(!mFallbackFailErrorShown) {
 | 
			
		||||
                    ALOGE("invalid parameters in TimestretchBufferProvider fallbackMode:%d",
 | 
			
		||||
                            mPlaybackRate.mFallbackMode);
 | 
			
		||||
                    mFallbackFailErrorShown = true;
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        switch (mFormat) {
 | 
			
		||||
        case AUDIO_FORMAT_PCM_FLOAT:
 | 
			
		||||
            if (sonicWriteFloatToStream(mSonicStream, (float*)srcBuffer, *srcFrames) != 1) {
 | 
			
		||||
                ALOGE("sonicWriteFloatToStream cannot realloc");
 | 
			
		||||
                *srcFrames = 0; // cannot consume all of srcBuffer
 | 
			
		||||
            }
 | 
			
		||||
            *dstFrames = sonicReadFloatFromStream(mSonicStream, (float*)dstBuffer, *dstFrames);
 | 
			
		||||
            break;
 | 
			
		||||
        case AUDIO_FORMAT_PCM_16_BIT:
 | 
			
		||||
            if (sonicWriteShortToStream(mSonicStream, (short*)srcBuffer, *srcFrames) != 1) {
 | 
			
		||||
                ALOGE("sonicWriteShortToStream cannot realloc");
 | 
			
		||||
                *srcFrames = 0; // cannot consume all of srcBuffer
 | 
			
		||||
            }
 | 
			
		||||
            *dstFrames = sonicReadShortFromStream(mSonicStream, (short*)dstBuffer, *dstFrames);
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            // could also be caught on construction
 | 
			
		||||
            LOG_ALWAYS_FATAL("invalid format %#x for TimestretchBufferProvider", mFormat);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
// ----------------------------------------------------------------------------
 | 
			
		||||
} // namespace android
 | 
			
		||||
@ -1,19 +1,3 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2014 The Android Open Source Project
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
@ -39,7 +23,7 @@ static void usage(const char* name) {
 | 
			
		||||
    fprintf(stderr, "Usage: %s [-f] [-m] [-c channels]"
 | 
			
		||||
                    " [-s sample-rate] [-o <output-file>] [-a <aux-buffer-file>] [-P csv]"
 | 
			
		||||
                    " (<input-file> | <command>)+\n", name);
 | 
			
		||||
    fprintf(stderr, "    -f    enable floating point input track\n");
 | 
			
		||||
    fprintf(stderr, "    -f    enable floating point input track by default\n");
 | 
			
		||||
    fprintf(stderr, "    -m    enable floating point mixer output\n");
 | 
			
		||||
    fprintf(stderr, "    -c    number of mixer output channels\n");
 | 
			
		||||
    fprintf(stderr, "    -s    mixer sample-rate\n");
 | 
			
		||||
@ -47,8 +31,8 @@ static void usage(const char* name) {
 | 
			
		||||
    fprintf(stderr, "    -a    <aux-buffer-file>\n");
 | 
			
		||||
    fprintf(stderr, "    -P    # frames provided per call to resample() in CSV format\n");
 | 
			
		||||
    fprintf(stderr, "    <input-file> is a WAV file\n");
 | 
			
		||||
    fprintf(stderr, "    <command> can be 'sine:<channels>,<frequency>,<samplerate>'\n");
 | 
			
		||||
    fprintf(stderr, "                     'chirp:<channels>,<samplerate>'\n");
 | 
			
		||||
    fprintf(stderr, "    <command> can be 'sine:[(i|f),]<channels>,<frequency>,<samplerate>'\n");
 | 
			
		||||
    fprintf(stderr, "                     'chirp:[(i|f),]<channels>,<samplerate>'\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int writeFile(const char *filename, const void *buffer,
 | 
			
		||||
@ -78,6 +62,18 @@ static int writeFile(const char *filename, const void *buffer,
 | 
			
		||||
    return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *parseFormat(const char *s, bool *useFloat) {
 | 
			
		||||
    if (!strncmp(s, "f,", 2)) {
 | 
			
		||||
        *useFloat = true;
 | 
			
		||||
        return s + 2;
 | 
			
		||||
    }
 | 
			
		||||
    if (!strncmp(s, "i,", 2)) {
 | 
			
		||||
        *useFloat = false;
 | 
			
		||||
        return s + 2;
 | 
			
		||||
    }
 | 
			
		||||
    return s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[]) {
 | 
			
		||||
    const char* const progname = argv[0];
 | 
			
		||||
    bool useInputFloat = false;
 | 
			
		||||
@ -88,8 +84,9 @@ int main(int argc, char* argv[]) {
 | 
			
		||||
    std::vector<int> Pvalues;
 | 
			
		||||
    const char* outputFilename = NULL;
 | 
			
		||||
    const char* auxFilename = NULL;
 | 
			
		||||
    std::vector<int32_t> Names;
 | 
			
		||||
    std::vector<SignalProvider> Providers;
 | 
			
		||||
    std::vector<int32_t> names;
 | 
			
		||||
    std::vector<SignalProvider> providers;
 | 
			
		||||
    std::vector<audio_format_t> formats;
 | 
			
		||||
 | 
			
		||||
    for (int ch; (ch = getopt(argc, argv, "fmc:s:o:a:P:")) != -1;) {
 | 
			
		||||
        switch (ch) {
 | 
			
		||||
@ -138,54 +135,65 @@ int main(int argc, char* argv[]) {
 | 
			
		||||
    size_t outputFrames = 0;
 | 
			
		||||
 | 
			
		||||
    // create providers for each track
 | 
			
		||||
    Providers.resize(argc);
 | 
			
		||||
    names.resize(argc);
 | 
			
		||||
    providers.resize(argc);
 | 
			
		||||
    formats.resize(argc);
 | 
			
		||||
    for (int i = 0; i < argc; ++i) {
 | 
			
		||||
        static const char chirp[] = "chirp:";
 | 
			
		||||
        static const char sine[] = "sine:";
 | 
			
		||||
        static const double kSeconds = 10;
 | 
			
		||||
        static const double kSeconds = 1;
 | 
			
		||||
        bool useFloat = useInputFloat;
 | 
			
		||||
 | 
			
		||||
        if (!strncmp(argv[i], chirp, strlen(chirp))) {
 | 
			
		||||
            std::vector<int> v;
 | 
			
		||||
            const char *s = parseFormat(argv[i] + strlen(chirp), &useFloat);
 | 
			
		||||
 | 
			
		||||
            parseCSV(argv[i] + strlen(chirp), v);
 | 
			
		||||
            parseCSV(s, v);
 | 
			
		||||
            if (v.size() == 2) {
 | 
			
		||||
                printf("creating chirp(%d %d)\n", v[0], v[1]);
 | 
			
		||||
                if (useInputFloat) {
 | 
			
		||||
                    Providers[i].setChirp<float>(v[0], 0, v[1]/2, v[1], kSeconds);
 | 
			
		||||
                if (useFloat) {
 | 
			
		||||
                    providers[i].setChirp<float>(v[0], 0, v[1]/2, v[1], kSeconds);
 | 
			
		||||
                    formats[i] = AUDIO_FORMAT_PCM_FLOAT;
 | 
			
		||||
                } else {
 | 
			
		||||
                    Providers[i].setChirp<int16_t>(v[0], 0, v[1]/2, v[1], kSeconds);
 | 
			
		||||
                    providers[i].setChirp<int16_t>(v[0], 0, v[1]/2, v[1], kSeconds);
 | 
			
		||||
                    formats[i] = AUDIO_FORMAT_PCM_16_BIT;
 | 
			
		||||
                }
 | 
			
		||||
                Providers[i].setIncr(Pvalues);
 | 
			
		||||
                providers[i].setIncr(Pvalues);
 | 
			
		||||
            } else {
 | 
			
		||||
                fprintf(stderr, "malformed input '%s'\n", argv[i]);
 | 
			
		||||
            }
 | 
			
		||||
        } else if (!strncmp(argv[i], sine, strlen(sine))) {
 | 
			
		||||
            std::vector<int> v;
 | 
			
		||||
            const char *s = parseFormat(argv[i] + strlen(sine), &useFloat);
 | 
			
		||||
 | 
			
		||||
            parseCSV(argv[i] + strlen(sine), v);
 | 
			
		||||
            parseCSV(s, v);
 | 
			
		||||
            if (v.size() == 3) {
 | 
			
		||||
                printf("creating sine(%d %d %d)\n", v[0], v[1], v[2]);
 | 
			
		||||
                if (useInputFloat) {
 | 
			
		||||
                    Providers[i].setSine<float>(v[0], v[1], v[2], kSeconds);
 | 
			
		||||
                if (useFloat) {
 | 
			
		||||
                    providers[i].setSine<float>(v[0], v[1], v[2], kSeconds);
 | 
			
		||||
                    formats[i] = AUDIO_FORMAT_PCM_FLOAT;
 | 
			
		||||
                } else {
 | 
			
		||||
                    Providers[i].setSine<int16_t>(v[0], v[1], v[2], kSeconds);
 | 
			
		||||
                    providers[i].setSine<int16_t>(v[0], v[1], v[2], kSeconds);
 | 
			
		||||
                    formats[i] = AUDIO_FORMAT_PCM_16_BIT;
 | 
			
		||||
                }
 | 
			
		||||
                Providers[i].setIncr(Pvalues);
 | 
			
		||||
                providers[i].setIncr(Pvalues);
 | 
			
		||||
            } else {
 | 
			
		||||
                fprintf(stderr, "malformed input '%s'\n", argv[i]);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            printf("creating filename(%s)\n", argv[i]);
 | 
			
		||||
            if (useInputFloat) {
 | 
			
		||||
                Providers[i].setFile<float>(argv[i]);
 | 
			
		||||
                providers[i].setFile<float>(argv[i]);
 | 
			
		||||
                formats[i] = AUDIO_FORMAT_PCM_FLOAT;
 | 
			
		||||
            } else {
 | 
			
		||||
                Providers[i].setFile<short>(argv[i]);
 | 
			
		||||
                providers[i].setFile<short>(argv[i]);
 | 
			
		||||
                formats[i] = AUDIO_FORMAT_PCM_16_BIT;
 | 
			
		||||
            }
 | 
			
		||||
            Providers[i].setIncr(Pvalues);
 | 
			
		||||
            providers[i].setIncr(Pvalues);
 | 
			
		||||
        }
 | 
			
		||||
        // calculate the number of output frames
 | 
			
		||||
        size_t nframes = (int64_t) Providers[i].getNumFrames() * outputSampleRate
 | 
			
		||||
                / Providers[i].getSampleRate();
 | 
			
		||||
        size_t nframes = (int64_t) providers[i].getNumFrames() * outputSampleRate
 | 
			
		||||
                / providers[i].getSampleRate();
 | 
			
		||||
        if (i == 0 || outputFrames > nframes) { // choose minimum for outputFrames
 | 
			
		||||
            outputFrames = nframes;
 | 
			
		||||
        }
 | 
			
		||||
@ -213,22 +221,20 @@ int main(int argc, char* argv[]) {
 | 
			
		||||
    // create the mixer.
 | 
			
		||||
    const size_t mixerFrameCount = 320; // typical numbers may range from 240 or 960
 | 
			
		||||
    AudioMixer *mixer = new AudioMixer(mixerFrameCount, outputSampleRate);
 | 
			
		||||
    audio_format_t inputFormat = useInputFloat
 | 
			
		||||
            ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
 | 
			
		||||
    audio_format_t mixerFormat = useMixerFloat
 | 
			
		||||
            ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
 | 
			
		||||
    float f = AudioMixer::UNITY_GAIN_FLOAT / Providers.size(); // normalize volume by # tracks
 | 
			
		||||
    float f = AudioMixer::UNITY_GAIN_FLOAT / providers.size(); // normalize volume by # tracks
 | 
			
		||||
    static float f0; // zero
 | 
			
		||||
 | 
			
		||||
    // set up the tracks.
 | 
			
		||||
    for (size_t i = 0; i < Providers.size(); ++i) {
 | 
			
		||||
        //printf("track %d out of %d\n", i, Providers.size());
 | 
			
		||||
        uint32_t channelMask = audio_channel_out_mask_from_count(Providers[i].getNumChannels());
 | 
			
		||||
    for (size_t i = 0; i < providers.size(); ++i) {
 | 
			
		||||
        //printf("track %d out of %d\n", i, providers.size());
 | 
			
		||||
        uint32_t channelMask = audio_channel_out_mask_from_count(providers[i].getNumChannels());
 | 
			
		||||
        int32_t name = mixer->getTrackName(channelMask,
 | 
			
		||||
                inputFormat, AUDIO_SESSION_OUTPUT_MIX);
 | 
			
		||||
                formats[i], AUDIO_SESSION_OUTPUT_MIX);
 | 
			
		||||
        ALOG_ASSERT(name >= 0);
 | 
			
		||||
        Names.push_back(name);
 | 
			
		||||
        mixer->setBufferProvider(name, &Providers[i]);
 | 
			
		||||
        names[i] = name;
 | 
			
		||||
        mixer->setBufferProvider(name, &providers[i]);
 | 
			
		||||
        mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
 | 
			
		||||
                (void *)outputAddr);
 | 
			
		||||
        mixer->setParameter(
 | 
			
		||||
@ -240,7 +246,7 @@ int main(int argc, char* argv[]) {
 | 
			
		||||
                name,
 | 
			
		||||
                AudioMixer::TRACK,
 | 
			
		||||
                AudioMixer::FORMAT,
 | 
			
		||||
                (void *)(uintptr_t)inputFormat);
 | 
			
		||||
                (void *)(uintptr_t)formats[i]);
 | 
			
		||||
        mixer->setParameter(
 | 
			
		||||
                name,
 | 
			
		||||
                AudioMixer::TRACK,
 | 
			
		||||
@ -255,7 +261,7 @@ int main(int argc, char* argv[]) {
 | 
			
		||||
                name,
 | 
			
		||||
                AudioMixer::RESAMPLE,
 | 
			
		||||
                AudioMixer::SAMPLE_RATE,
 | 
			
		||||
                (void *)(uintptr_t)Providers[i].getSampleRate());
 | 
			
		||||
                (void *)(uintptr_t)providers[i].getSampleRate());
 | 
			
		||||
        if (useRamp) {
 | 
			
		||||
            mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f0);
 | 
			
		||||
            mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f0);
 | 
			
		||||
@ -277,11 +283,11 @@ int main(int argc, char* argv[]) {
 | 
			
		||||
    // pump the mixer to process data.
 | 
			
		||||
    size_t i;
 | 
			
		||||
    for (i = 0; i < outputFrames - mixerFrameCount; i += mixerFrameCount) {
 | 
			
		||||
        for (size_t j = 0; j < Names.size(); ++j) {
 | 
			
		||||
            mixer->setParameter(Names[j], AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
 | 
			
		||||
        for (size_t j = 0; j < names.size(); ++j) {
 | 
			
		||||
            mixer->setParameter(names[j], AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
 | 
			
		||||
                    (char *) outputAddr + i * outputFrameSize);
 | 
			
		||||
            if (auxFilename) {
 | 
			
		||||
                mixer->setParameter(Names[j], AudioMixer::TRACK, AudioMixer::AUX_BUFFER,
 | 
			
		||||
                mixer->setParameter(names[j], AudioMixer::TRACK, AudioMixer::AUX_BUFFER,
 | 
			
		||||
                        (char *) auxAddr + i * auxFrameSize);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user