1
0
mirror of https://github.com/ioacademy-jikim/multimedia synced 2025-06-07 16:06:18 +00:00

오디오 믹서수정

This commit is contained in:
juning kim 2015-11-01 02:00:47 +09:00
parent 2e8959101d
commit cd8732f3b1
5 changed files with 931 additions and 592 deletions

View File

@ -1,24 +1,18 @@
LOCAL_PATH:= $(call my-dir) LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \ LOCAL_SRC_FILES:= test-mixer.cpp AudioMixer.cpp.arm BufferProviders.cpp
test-mixer.cpp \
AudioMixer.cpp.arm \
LOCAL_C_INCLUDES := \ LOCAL_C_INCLUDES := \
bionic \
bionic/libstdc++/include \
external/stlport/stlport \
$(call include-path-for, audio-effects) \ $(call include-path-for, audio-effects) \
$(call include-path-for, audio-utils) \ $(call include-path-for, audio-utils) \
frameworks/av/services/audioflinger frameworks/av/services/audioflinger \
external/sonic
LOCAL_STATIC_LIBRARIES := \ LOCAL_STATIC_LIBRARIES := \
libsndfile libsndfile
LOCAL_SHARED_LIBRARIES := \ LOCAL_SHARED_LIBRARIES := \
libstlport \
libeffects \ libeffects \
libnbaio \ libnbaio \
libcommon_time_client \ libcommon_time_client \
@ -27,10 +21,13 @@ LOCAL_SHARED_LIBRARIES := \
libdl \ libdl \
libcutils \ libcutils \
libutils \ libutils \
liblog liblog \
libsonic
LOCAL_MODULE:= test-mixer LOCAL_MODULE:= test-mixer
LOCAL_MODULE_TAGS := optional LOCAL_MODULE_TAGS := optional
LOCAL_CXX_STL := libc++
include $(BUILD_EXECUTABLE) include $(BUILD_EXECUTABLE)

View File

@ -39,9 +39,6 @@
#include <common_time/local_clock.h> #include <common_time/local_clock.h>
#include <common_time/cc_helper.h> #include <common_time/cc_helper.h>
#include <media/EffectsFactoryApi.h>
#include <audio_effects/effect_downmix.h>
#include "AudioMixerOps.h" #include "AudioMixerOps.h"
#include "AudioMixer.h" #include "AudioMixer.h"
@ -69,9 +66,16 @@
#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
#endif #endif
// Set kUseNewMixer to true to use the new mixer engine. Otherwise the // TODO: Move these macro/inlines to a header file.
// original code will be used. This is false for now. template <typename T>
static const bool kUseNewMixer = false; 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. // Set kUseFloat to true to allow floating input into the mixer engine.
// If kUseNewMixer is false, this is ignored or may be overridden internally // 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; 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. // 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->resampler = NULL;
t->downmixerBufferProvider = NULL; t->downmixerBufferProvider = NULL;
t->mReformatBufferProvider = NULL; t->mReformatBufferProvider = NULL;
t->mTimestretchBufferProvider = NULL;
t++; t++;
} }
@ -419,6 +142,7 @@ AudioMixer::~AudioMixer()
delete t->resampler; delete t->resampler;
delete t->downmixerBufferProvider; delete t->downmixerBufferProvider;
delete t->mReformatBufferProvider; delete t->mReformatBufferProvider;
delete t->mTimestretchBufferProvider;
t++; t++;
} }
delete [] mState.outputTemp; delete [] mState.outputTemp;
@ -430,6 +154,10 @@ void AudioMixer::setLog(NBLog::Writer *log)
mState.mLog = 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, int AudioMixer::getTrackName(audio_channel_mask_t channelMask,
audio_format_t format, int sessionId) audio_format_t format, int sessionId)
{ {
@ -492,24 +220,25 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask,
t->mInputBufferProvider = NULL; t->mInputBufferProvider = NULL;
t->mReformatBufferProvider = NULL; t->mReformatBufferProvider = NULL;
t->downmixerBufferProvider = NULL; t->downmixerBufferProvider = NULL;
t->mPostDownmixReformatBufferProvider = NULL;
t->mTimestretchBufferProvider = NULL;
t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT; t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
t->mFormat = format; t->mFormat = format;
t->mMixerInFormat = kUseFloat && kUseNewMixer t->mMixerInFormat = selectMixerInFormat(format);
? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; t->mDownmixRequiresFormat = AUDIO_FORMAT_INVALID; // no format required
t->mMixerChannelMask = audio_channel_mask_from_representation_and_bits( t->mMixerChannelMask = audio_channel_mask_from_representation_and_bits(
AUDIO_CHANNEL_REPRESENTATION_POSITION, AUDIO_CHANNEL_OUT_STEREO); AUDIO_CHANNEL_REPRESENTATION_POSITION, AUDIO_CHANNEL_OUT_STEREO);
t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask); t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask);
t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
// Check the downmixing (or upmixing) requirements. // Check the downmixing (or upmixing) requirements.
status_t status = initTrackDownmix(t, n); status_t status = t->prepareForDownmix();
if (status != OK) { if (status != OK) {
ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask); ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask);
return -1; return -1;
} }
// initTrackDownmix() may change the input format requirement. // prepareForDownmix() may change mDownmixRequiresFormat
// If you desire floating point input to the mixer, it may change
// to integer because the downmixer requires integer to process.
ALOGVV("mMixerFormat:%#x mMixerInFormat:%#x\n", t->mMixerFormat, t->mMixerInFormat); ALOGVV("mMixerFormat:%#x mMixerInFormat:%#x\n", t->mMixerFormat, t->mMixerInFormat);
prepareTrackForReformat(t, n); t->prepareForReformat();
mTrackNames |= 1 << n; mTrackNames |= 1 << n;
return TRACK0 + n; return TRACK0 + n;
} }
@ -526,7 +255,7 @@ void AudioMixer::invalidateState(uint32_t mask)
} }
// Called when channel masks have changed for a track name // 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. // which will simplify this logic.
bool AudioMixer::setChannelMasks(int name, bool AudioMixer::setChannelMasks(int name,
audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask) { 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? // channel masks have changed, does this track need a downmixer?
// update to try using our desired format (if we aren't already using it) // update to try using our desired format (if we aren't already using it)
const audio_format_t prevMixerInFormat = track.mMixerInFormat; const audio_format_t prevDownmixerFormat = track.mDownmixRequiresFormat;
track.mMixerInFormat = kUseFloat && kUseNewMixer const status_t status = mState.tracks[name].prepareForDownmix();
? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
const status_t status = initTrackDownmix(&mState.tracks[name], name);
ALOGE_IF(status != OK, 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); status, track.channelMask, track.mMixerChannelMask);
const bool mixerInFormatChanged = prevMixerInFormat != track.mMixerInFormat; if (prevDownmixerFormat != track.mDownmixRequiresFormat) {
if (mixerInFormatChanged) { track.prepareForReformat(); // because of downmixer, track format may change!
prepareTrackForReformat(&track, name); // because of downmixer, track format may change!
} }
if (track.resampler && (mixerInFormatChanged || mixerChannelCountChanged)) { if (track.resampler && mixerChannelCountChanged) {
// resampler input format or channels may have changed. // resampler channels may have changed.
const uint32_t resetToSampleRate = track.sampleRate; const uint32_t resetToSampleRate = track.sampleRate;
delete track.resampler; delete track.resampler;
track.resampler = NULL; track.resampler = NULL;
@ -576,99 +302,129 @@ bool AudioMixer::setChannelMasks(int name,
return true; return true;
} }
status_t AudioMixer::initTrackDownmix(track_t* pTrack, int trackName) void AudioMixer::track_t::unprepareForDownmix() {
{ ALOGV("AudioMixer::unprepareForDownmix(%p)", this);
// 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::unprepareTrackForDownmix(track_t* pTrack, int trackName __unused) { mDownmixRequiresFormat = AUDIO_FORMAT_INVALID;
ALOGV("AudioMixer::unprepareTrackForDownmix(%d)", trackName); if (downmixerBufferProvider != NULL) {
if (pTrack->downmixerBufferProvider != NULL) {
// this track had previously been configured with a downmixer, delete it // this track had previously been configured with a downmixer, delete it
ALOGV(" deleting old downmixer"); ALOGV(" deleting old downmixer");
delete pTrack->downmixerBufferProvider; delete downmixerBufferProvider;
pTrack->downmixerBufferProvider = NULL; downmixerBufferProvider = NULL;
reconfigureBufferProviders(pTrack); reconfigureBufferProviders();
} else { } else {
ALOGV(" nothing to do, no downmixer to delete"); 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 // discard the previous downmixer if there was one
unprepareTrackForDownmix(pTrack, trackName); unprepareForDownmix();
if (DownmixerBufferProvider::isMultichannelCapable()) { // MONO_HACK Only remix (upmix or downmix) if the track and mixer/device channel masks
DownmixerBufferProvider* pDbp = new DownmixerBufferProvider(pTrack->channelMask, // are not the same and not handled internally, as mono -> stereo currently is.
pTrack->mMixerChannelMask, if (channelMask == mMixerChannelMask
AUDIO_FORMAT_PCM_16_BIT /* TODO: use pTrack->mMixerInFormat, now only PCM 16 */, || (channelMask == AUDIO_CHANNEL_OUT_MONO
pTrack->sampleRate, pTrack->sessionId, kCopyBufferFrameCount); && 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 if (pDbp->isValid()) { // if constructor completed properly
pTrack->mMixerInFormat = AUDIO_FORMAT_PCM_16_BIT; // PCM 16 bit required for downmix mDownmixRequiresFormat = AUDIO_FORMAT_PCM_16_BIT; // PCM 16 bit required for downmix
pTrack->downmixerBufferProvider = pDbp; downmixerBufferProvider = pDbp;
reconfigureBufferProviders(pTrack); reconfigureBufferProviders();
return NO_ERROR; return NO_ERROR;
} }
delete pDbp; delete pDbp;
} }
// Effect downmixer does not accept the channel conversion. Let's use our remixer. // Effect downmixer does not accept the channel conversion. Let's use our remixer.
RemixBufferProvider* pRbp = new RemixBufferProvider(pTrack->channelMask, RemixBufferProvider* pRbp = new RemixBufferProvider(channelMask,
pTrack->mMixerChannelMask, pTrack->mMixerInFormat, kCopyBufferFrameCount); mMixerChannelMask, mMixerInFormat, kCopyBufferFrameCount);
// Remix always finds a conversion whereas Downmixer effect above may fail. // Remix always finds a conversion whereas Downmixer effect above may fail.
pTrack->downmixerBufferProvider = pRbp; downmixerBufferProvider = pRbp;
reconfigureBufferProviders(pTrack); reconfigureBufferProviders();
return NO_ERROR; return NO_ERROR;
} }
void AudioMixer::unprepareTrackForReformat(track_t* pTrack, int trackName __unused) { void AudioMixer::track_t::unprepareForReformat() {
ALOGV("AudioMixer::unprepareTrackForReformat(%d)", trackName); ALOGV("AudioMixer::unprepareForReformat(%p)", this);
if (pTrack->mReformatBufferProvider != NULL) { bool requiresReconfigure = false;
delete pTrack->mReformatBufferProvider; if (mReformatBufferProvider != NULL) {
pTrack->mReformatBufferProvider = NULL; delete mReformatBufferProvider;
reconfigureBufferProviders(pTrack); 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); ALOGV("AudioMixer::prepareForReformat(%p) with format %#x", this, mFormat);
// discard the previous reformatter if there was one // discard previous reformatters
unprepareTrackForReformat(pTrack, trackName); unprepareForReformat();
// only configure reformatter if needed // only configure reformatters as needed
if (pTrack->mFormat != pTrack->mMixerInFormat) { const audio_format_t targetFormat = mDownmixRequiresFormat != AUDIO_FORMAT_INVALID
pTrack->mReformatBufferProvider = new ReformatBufferProvider( ? mDownmixRequiresFormat : mMixerInFormat;
audio_channel_count_from_out_mask(pTrack->channelMask), bool requiresReconfigure = false;
pTrack->mFormat, pTrack->mMixerInFormat, if (mFormat != targetFormat) {
mReformatBufferProvider = new ReformatBufferProvider(
audio_channel_count_from_out_mask(channelMask),
mFormat,
targetFormat,
kCopyBufferFrameCount); 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; return NO_ERROR;
} }
void AudioMixer::reconfigureBufferProviders(track_t* pTrack) void AudioMixer::track_t::reconfigureBufferProviders()
{ {
pTrack->bufferProvider = pTrack->mInputBufferProvider; bufferProvider = mInputBufferProvider;
if (pTrack->mReformatBufferProvider) { if (mReformatBufferProvider) {
pTrack->mReformatBufferProvider->setBufferProvider(pTrack->bufferProvider); mReformatBufferProvider->setBufferProvider(bufferProvider);
pTrack->bufferProvider = pTrack->mReformatBufferProvider; bufferProvider = mReformatBufferProvider;
} }
if (pTrack->downmixerBufferProvider) { if (downmixerBufferProvider) {
pTrack->downmixerBufferProvider->setBufferProvider(pTrack->bufferProvider); downmixerBufferProvider->setBufferProvider(bufferProvider);
pTrack->bufferProvider = pTrack->downmixerBufferProvider; 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; delete track.resampler;
track.resampler = NULL; track.resampler = NULL;
// delete the downmixer // delete the downmixer
unprepareTrackForDownmix(&mState.tracks[name], name); mState.tracks[name].unprepareForDownmix();
// delete the reformatter // 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); mTrackNames &= ~(1<<name);
} }
@ -748,41 +506,99 @@ void AudioMixer::disable(int name)
static inline bool setVolumeRampVariables(float newVolume, int32_t ramp, static inline bool setVolumeRampVariables(float newVolume, int32_t ramp,
int16_t *pIntSetVolume, int32_t *pIntPrevVolume, int32_t *pIntVolumeInc, int16_t *pIntSetVolume, int32_t *pIntPrevVolume, int32_t *pIntVolumeInc,
float *pSetVolume, float *pPrevVolume, float *pVolumeInc) { 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) { if (newVolume == *pSetVolume) {
return false; return false;
} }
/* set the floating point volume variables */ if (newVolume < 0) {
if (ramp != 0) { newVolume = 0; // should not have negative volumes
*pVolumeInc = (newVolume - *pSetVolume) / ramp;
*pPrevVolume = *pSetVolume;
} else { } else {
*pVolumeInc = 0; switch (fpclassify(newVolume)) {
*pPrevVolume = 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 */ // set floating point volume ramp
int32_t intVolume = newVolume * AudioMixer::UNITY_GAIN_INT; if (ramp != 0) {
if (intVolume > AudioMixer::UNITY_GAIN_INT) { // when the ramp completes, *pPrevVolume is set to *pSetVolume, so there
intVolume = AudioMixer::UNITY_GAIN_INT; // is no computational mismatch; hence equality is checked here.
} else if (intVolume < 0) { ALOGD_IF(*pPrevVolume != *pSetVolume, "previous float ramp hasn't finished,"
ALOGE("negative volume %.7g", newVolume); " prev:%f set_to:%f", *pPrevVolume, *pSetVolume);
intVolume = 0; // should never happen, but for safety check. 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; // compute and check integer volume, no need to check negative values
/* TODO: integer/float workaround: ignore floating volume ramp */ // 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; *pVolumeInc = 0;
*pPrevVolume = newVolume; *pPrevVolume = newVolume;
return true;
}
if (ramp != 0) {
*pIntVolumeInc = ((intVolume - *pIntSetVolume) << 16) / ramp;
*pIntPrevVolume = (*pIntVolumeInc == 0 ? intVolume : *pIntSetVolume) << 16;
} else {
*pIntVolumeInc = 0; *pIntVolumeInc = 0;
*pIntPrevVolume = intVolume << 16; *pIntPrevVolume = intVolume << 16;
} }
*pSetVolume = newVolume;
*pIntSetVolume = intVolume; *pIntSetVolume = intVolume;
return true; 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); ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format);
track.mFormat = format; track.mFormat = format;
ALOGV("setParameter(TRACK, FORMAT, %#x)", format); ALOGV("setParameter(TRACK, FORMAT, %#x)", format);
prepareTrackForReformat(&track, name); track.prepareForReformat();
invalidateState(1 << name); invalidateState(1 << name);
} }
} break; } break;
@ -912,6 +728,28 @@ void AudioMixer::setParameter(int name, int target, int param, void *value)
} }
} }
break; 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: default:
LOG_ALWAYS_FATAL("setParameter: bad target %d", target); 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 // 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. // 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. // Should have a way to distinguish tracks with static ratios vs. dynamic ratios.
if (!((trackSampleRate == 44100 && devSampleRate == 48000) || if (isMusicRate(trackSampleRate)) {
(trackSampleRate == 48000 && devSampleRate == 44100))) {
quality = AudioResampler::DYN_LOW_QUALITY;
} else {
quality = AudioResampler::DEFAULT_QUALITY; quality = AudioResampler::DEFAULT_QUALITY;
} else {
quality = AudioResampler::DYN_LOW_QUALITY;
} }
// TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer // 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; 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 /* Checks to see if the volume ramp has completed and clears the increment
* variables appropriately. * variables appropriately.
* *
@ -974,7 +835,8 @@ inline void AudioMixer::track_t::adjustVolumeRamp(bool aux, bool useFloat)
{ {
if (useFloat) { if (useFloat) {
for (uint32_t i = 0; i < MAX_NUM_VOLUMES; i++) { 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; volumeInc[i] = 0;
prevVolume[i] = volume[i] << 16; prevVolume[i] = volume[i] << 16;
mVolumeInc[i] = 0.; mVolumeInc[i] = 0.;
@ -1032,10 +894,15 @@ void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider
if (mState.tracks[name].mReformatBufferProvider != NULL) { if (mState.tracks[name].mReformatBufferProvider != NULL) {
mState.tracks[name].mReformatBufferProvider->reset(); mState.tracks[name].mReformatBufferProvider->reset();
} else if (mState.tracks[name].downmixerBufferProvider != NULL) { } 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; 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 { } else {
if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){ if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
t.hook = getTrackHook( 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, ? TRACKTYPE_NORESAMPLEMONO : TRACKTYPE_NORESAMPLE,
t.mMixerChannelCount, t.mMixerChannelCount,
t.mMixerInFormat, t.mMixerFormat); t.mMixerInFormat, t.mMixerFormat);
@ -2236,4 +2104,4 @@ AudioMixer::process_hook_t AudioMixer::getProcessHook(int processType, uint32_t
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
}; // namespace android } // namespace android

View File

@ -21,14 +21,16 @@
#include <stdint.h> #include <stdint.h>
#include <sys/types.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 <utils/threads.h>
#include <media/AudioBufferProvider.h>
#include "AudioResampler.h" #include "AudioResampler.h"
#include "BufferProviders.h"
#include <hardware/audio_effect.h>
#include <system/audio.h>
#include <media/nbaio/NBLog.h>
// FIXME This is actually unity gain, which might not be max in future, expressed in U.12 // 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 #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 uint32_t MAX_NUM_CHANNELS_TO_DOWNMIX = AUDIO_CHANNEL_COUNT_MAX;
static const uint16_t UNITY_GAIN_INT = 0x1000; 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 enum { // names
@ -72,6 +74,7 @@ public:
RESAMPLE = 0x3001, RESAMPLE = 0x3001,
RAMP_VOLUME = 0x3002, // ramp to new volume RAMP_VOLUME = 0x3002, // ramp to new volume
VOLUME = 0x3003, // don't ramp VOLUME = 0x3003, // don't ramp
TIMESTRETCH = 0x3004,
// set Parameter names // set Parameter names
// for target TRACK // for target TRACK
@ -99,6 +102,9 @@ public:
VOLUME0 = 0x4200, VOLUME0 = 0x4200,
VOLUME1 = 0x4201, VOLUME1 = 0x4201,
AUXLEVEL = 0x4210, 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; size_t getUnreleasedFrames(int name) const;
static inline bool isValidPcmTrackFormat(audio_format_t format) { static inline bool isValidPcmTrackFormat(audio_format_t format) {
return format == AUDIO_FORMAT_PCM_16_BIT || switch (format) {
format == AUDIO_FORMAT_PCM_24_BIT_PACKED || case AUDIO_FORMAT_PCM_8_BIT:
format == AUDIO_FORMAT_PCM_32_BIT || case AUDIO_FORMAT_PCM_16_BIT:
format == AUDIO_FORMAT_PCM_FLOAT; case AUDIO_FORMAT_PCM_24_BIT_PACKED:
case AUDIO_FORMAT_PCM_32_BIT:
case AUDIO_FORMAT_PCM_FLOAT:
return true;
default:
return false;
}
} }
private: private:
@ -153,7 +165,6 @@ private:
struct state_t; struct state_t;
struct track_t; struct track_t;
class CopyBufferProvider;
typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp, typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp,
int32_t* aux); int32_t* aux);
@ -205,17 +216,38 @@ private:
int32_t* auxBuffer; int32_t* auxBuffer;
// 16-byte boundary // 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. AudioBufferProvider* mInputBufferProvider; // externally provided buffer provider.
CopyBufferProvider* mReformatBufferProvider; // provider wrapper for reformatting. PassthruBufferProvider* mReformatBufferProvider; // provider wrapper for reformatting.
CopyBufferProvider* downmixerBufferProvider; // wrapper for channel conversion. PassthruBufferProvider* downmixerBufferProvider; // wrapper for channel conversion.
PassthruBufferProvider* mPostDownmixReformatBufferProvider;
PassthruBufferProvider* mTimestretchBufferProvider;
int32_t sessionId; int32_t sessionId;
// 16-byte boundary
audio_format_t mMixerFormat; // output mix format: AUDIO_FORMAT_PCM_(FLOAT|16_BIT) audio_format_t mMixerFormat; // output mix format: AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
audio_format_t mFormat; // input track format audio_format_t mFormat; // input track format
audio_format_t mMixerInFormat; // mix internal format AUDIO_FORMAT_PCM_(FLOAT|16_BIT) audio_format_t mMixerInFormat; // mix internal format AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
// each track must be converted to this format. // 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 mVolume[MAX_NUM_VOLUMES]; // floating point set volume
float mPrevVolume[MAX_NUM_VOLUMES]; // floating point previous volume float mPrevVolume[MAX_NUM_VOLUMES]; // floating point previous volume
@ -225,10 +257,11 @@ private:
float mPrevAuxLevel; // floating point prev aux level float mPrevAuxLevel; // floating point prev aux level
float mAuxInc; // floating point aux increment float mAuxInc; // floating point aux increment
// 16-byte boundary
audio_channel_mask_t mMixerChannelMask; audio_channel_mask_t mMixerChannelMask;
uint32_t mMixerChannelCount; uint32_t mMixerChannelCount;
AudioPlaybackRate mPlaybackRate;
bool needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; } bool needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; }
bool setResampler(uint32_t trackSampleRate, uint32_t devSampleRate); bool setResampler(uint32_t trackSampleRate, uint32_t devSampleRate);
bool doesResample() const { return resampler != NULL; } bool doesResample() const { return resampler != NULL; }
@ -236,6 +269,13 @@ private:
void adjustVolumeRamp(bool aux, bool useFloat = false); void adjustVolumeRamp(bool aux, bool useFloat = false);
size_t getUnreleasedFrames() const { return resampler != NULL ? size_t getUnreleasedFrames() const { return resampler != NULL ?
resampler->getUnreleasedFrames() : 0; }; 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); 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))); 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. // bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc.
uint32_t mTrackNames; uint32_t mTrackNames;
@ -382,14 +316,6 @@ private:
bool setChannelMasks(int name, bool setChannelMasks(int name,
audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask); 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, static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp,
int32_t* aux); int32_t* aux);
static void track__nop(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 #endif // ANDROID_AUDIO_MIXER_H

View 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

View File

@ -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 <stdio.h>
#include <inttypes.h> #include <inttypes.h>
#include <math.h> #include <math.h>
@ -39,7 +23,7 @@ static void usage(const char* name) {
fprintf(stderr, "Usage: %s [-f] [-m] [-c channels]" fprintf(stderr, "Usage: %s [-f] [-m] [-c channels]"
" [-s sample-rate] [-o <output-file>] [-a <aux-buffer-file>] [-P csv]" " [-s sample-rate] [-o <output-file>] [-a <aux-buffer-file>] [-P csv]"
" (<input-file> | <command>)+\n", name); " (<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, " -m enable floating point mixer output\n");
fprintf(stderr, " -c number of mixer output channels\n"); fprintf(stderr, " -c number of mixer output channels\n");
fprintf(stderr, " -s mixer sample-rate\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, " -a <aux-buffer-file>\n");
fprintf(stderr, " -P # frames provided per call to resample() in CSV format\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, " <input-file> is a WAV file\n");
fprintf(stderr, " <command> can be 'sine:<channels>,<frequency>,<samplerate>'\n"); fprintf(stderr, " <command> can be 'sine:[(i|f),]<channels>,<frequency>,<samplerate>'\n");
fprintf(stderr, " 'chirp:<channels>,<samplerate>'\n"); fprintf(stderr, " 'chirp:[(i|f),]<channels>,<samplerate>'\n");
} }
static int writeFile(const char *filename, const void *buffer, 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; 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[]) { int main(int argc, char* argv[]) {
const char* const progname = argv[0]; const char* const progname = argv[0];
bool useInputFloat = false; bool useInputFloat = false;
@ -88,8 +84,9 @@ int main(int argc, char* argv[]) {
std::vector<int> Pvalues; std::vector<int> Pvalues;
const char* outputFilename = NULL; const char* outputFilename = NULL;
const char* auxFilename = NULL; const char* auxFilename = NULL;
std::vector<int32_t> Names; std::vector<int32_t> names;
std::vector<SignalProvider> Providers; std::vector<SignalProvider> providers;
std::vector<audio_format_t> formats;
for (int ch; (ch = getopt(argc, argv, "fmc:s:o:a:P:")) != -1;) { for (int ch; (ch = getopt(argc, argv, "fmc:s:o:a:P:")) != -1;) {
switch (ch) { switch (ch) {
@ -138,54 +135,65 @@ int main(int argc, char* argv[]) {
size_t outputFrames = 0; size_t outputFrames = 0;
// create providers for each track // create providers for each track
Providers.resize(argc); names.resize(argc);
providers.resize(argc);
formats.resize(argc);
for (int i = 0; i < argc; ++i) { for (int i = 0; i < argc; ++i) {
static const char chirp[] = "chirp:"; static const char chirp[] = "chirp:";
static const char sine[] = "sine:"; 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))) { if (!strncmp(argv[i], chirp, strlen(chirp))) {
std::vector<int> v; 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) { if (v.size() == 2) {
printf("creating chirp(%d %d)\n", v[0], v[1]); printf("creating chirp(%d %d)\n", v[0], v[1]);
if (useInputFloat) { if (useFloat) {
Providers[i].setChirp<float>(v[0], 0, v[1]/2, v[1], kSeconds); providers[i].setChirp<float>(v[0], 0, v[1]/2, v[1], kSeconds);
formats[i] = AUDIO_FORMAT_PCM_FLOAT;
} else { } 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 { } else {
fprintf(stderr, "malformed input '%s'\n", argv[i]); fprintf(stderr, "malformed input '%s'\n", argv[i]);
} }
} else if (!strncmp(argv[i], sine, strlen(sine))) { } else if (!strncmp(argv[i], sine, strlen(sine))) {
std::vector<int> v; 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) { if (v.size() == 3) {
printf("creating sine(%d %d %d)\n", v[0], v[1], v[2]); printf("creating sine(%d %d %d)\n", v[0], v[1], v[2]);
if (useInputFloat) { if (useFloat) {
Providers[i].setSine<float>(v[0], v[1], v[2], kSeconds); providers[i].setSine<float>(v[0], v[1], v[2], kSeconds);
formats[i] = AUDIO_FORMAT_PCM_FLOAT;
} else { } 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 { } else {
fprintf(stderr, "malformed input '%s'\n", argv[i]); fprintf(stderr, "malformed input '%s'\n", argv[i]);
} }
} else { } else {
printf("creating filename(%s)\n", argv[i]); printf("creating filename(%s)\n", argv[i]);
if (useInputFloat) { if (useInputFloat) {
Providers[i].setFile<float>(argv[i]); providers[i].setFile<float>(argv[i]);
formats[i] = AUDIO_FORMAT_PCM_FLOAT;
} else { } 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 // calculate the number of output frames
size_t nframes = (int64_t) Providers[i].getNumFrames() * outputSampleRate size_t nframes = (int64_t) providers[i].getNumFrames() * outputSampleRate
/ Providers[i].getSampleRate(); / providers[i].getSampleRate();
if (i == 0 || outputFrames > nframes) { // choose minimum for outputFrames if (i == 0 || outputFrames > nframes) { // choose minimum for outputFrames
outputFrames = nframes; outputFrames = nframes;
} }
@ -213,22 +221,20 @@ int main(int argc, char* argv[]) {
// create the mixer. // create the mixer.
const size_t mixerFrameCount = 320; // typical numbers may range from 240 or 960 const size_t mixerFrameCount = 320; // typical numbers may range from 240 or 960
AudioMixer *mixer = new AudioMixer(mixerFrameCount, outputSampleRate); 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_t mixerFormat = useMixerFloat
? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; ? 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 static float f0; // zero
// set up the tracks. // set up the tracks.
for (size_t i = 0; i < Providers.size(); ++i) { for (size_t i = 0; i < providers.size(); ++i) {
//printf("track %d out of %d\n", i, Providers.size()); //printf("track %d out of %d\n", i, providers.size());
uint32_t channelMask = audio_channel_out_mask_from_count(Providers[i].getNumChannels()); uint32_t channelMask = audio_channel_out_mask_from_count(providers[i].getNumChannels());
int32_t name = mixer->getTrackName(channelMask, int32_t name = mixer->getTrackName(channelMask,
inputFormat, AUDIO_SESSION_OUTPUT_MIX); formats[i], AUDIO_SESSION_OUTPUT_MIX);
ALOG_ASSERT(name >= 0); ALOG_ASSERT(name >= 0);
Names.push_back(name); names[i] = name;
mixer->setBufferProvider(name, &Providers[i]); mixer->setBufferProvider(name, &providers[i]);
mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
(void *)outputAddr); (void *)outputAddr);
mixer->setParameter( mixer->setParameter(
@ -240,7 +246,7 @@ int main(int argc, char* argv[]) {
name, name,
AudioMixer::TRACK, AudioMixer::TRACK,
AudioMixer::FORMAT, AudioMixer::FORMAT,
(void *)(uintptr_t)inputFormat); (void *)(uintptr_t)formats[i]);
mixer->setParameter( mixer->setParameter(
name, name,
AudioMixer::TRACK, AudioMixer::TRACK,
@ -255,7 +261,7 @@ int main(int argc, char* argv[]) {
name, name,
AudioMixer::RESAMPLE, AudioMixer::RESAMPLE,
AudioMixer::SAMPLE_RATE, AudioMixer::SAMPLE_RATE,
(void *)(uintptr_t)Providers[i].getSampleRate()); (void *)(uintptr_t)providers[i].getSampleRate());
if (useRamp) { if (useRamp) {
mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f0); mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f0);
mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &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. // pump the mixer to process data.
size_t i; size_t i;
for (i = 0; i < outputFrames - mixerFrameCount; i += mixerFrameCount) { for (i = 0; i < outputFrames - mixerFrameCount; i += mixerFrameCount) {
for (size_t j = 0; j < Names.size(); ++j) { for (size_t j = 0; j < names.size(); ++j) {
mixer->setParameter(Names[j], AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, mixer->setParameter(names[j], AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
(char *) outputAddr + i * outputFrameSize); (char *) outputAddr + i * outputFrameSize);
if (auxFilename) { if (auxFilename) {
mixer->setParameter(Names[j], AudioMixer::TRACK, AudioMixer::AUX_BUFFER, mixer->setParameter(names[j], AudioMixer::TRACK, AudioMixer::AUX_BUFFER,
(char *) auxAddr + i * auxFrameSize); (char *) auxAddr + i * auxFrameSize);
} }
} }