blob: 345665cc54360b5d4f5348a69a6902b286788a5b [file] [log] [blame]
/*
**
** Copyright 2021, 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 "Spatializer"
//#define LOG_NDEBUG 0
#include <utils/Log.h>
#include <limits.h>
#include <stdint.h>
#include <sys/types.h>
#include <android/content/AttributionSourceState.h>
#include <audio_utils/fixedfft.h>
#include <cutils/bitops.h>
#include <media/ShmemCompat.h>
#include <media/audiohal/EffectsFactoryHalInterface.h>
#include <mediautils/ServiceUtilities.h>
#include <utils/Thread.h>
#include "Spatializer.h"
namespace android {
using aidl_utils::statusTFromBinderStatus;
using aidl_utils::binderStatusFromStatusT;
using android::content::AttributionSourceState;
using binder::Status;
using media::HeadTrackingMode;
using media::SpatializationLevel;
#define VALUE_OR_RETURN_BINDER_STATUS(x) \
({ auto _tmp = (x); \
if (!_tmp.ok()) return aidl_utils::binderStatusFromStatusT(_tmp.error()); \
std::move(_tmp.value()); })
#define RETURN_IF_BINDER_ERROR(x) \
{ \
binder::Status _tmp = (x); \
if (!_tmp.isOk()) return _tmp; \
}
// ---------------------------------------------------------------------------
sp<Spatializer> Spatializer::create(SpatializerPolicyCallback *callback) {
sp<Spatializer> spatializer;
sp<EffectsFactoryHalInterface> effectsFactoryHal = EffectsFactoryHalInterface::create();
if (effectsFactoryHal == nullptr) {
ALOGW("%s failed to create effect factory interface", __func__);
return spatializer;
}
std::vector<effect_descriptor_t> descriptors;
status_t status =
effectsFactoryHal->getDescriptors(FX_IID_SPATIALIZER, &descriptors);
if (status != NO_ERROR) {
ALOGW("%s failed to get spatializer descriptor, error %d", __func__, status);
return spatializer;
}
ALOG_ASSERT(!descriptors.empty(),
"%s getDescriptors() returned no error but empty list", __func__);
//TODO: get supported spatialization modes from FX engine or descriptor
sp<EffectHalInterface> effect;
status = effectsFactoryHal->createEffect(&descriptors[0].uuid, AUDIO_SESSION_OUTPUT_STAGE,
AUDIO_IO_HANDLE_NONE, AUDIO_PORT_HANDLE_NONE, &effect);
ALOGI("%s FX create status %d effect %p", __func__, status, effect.get());
if (status == NO_ERROR && effect != nullptr) {
spatializer = new Spatializer(descriptors[0], callback);
// TODO: Read supported config from engine
audio_config_base_t config = AUDIO_CONFIG_BASE_INITIALIZER;
config.channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
spatializer->setAudioInConfig(config);
}
return spatializer;
}
Spatializer::Spatializer(effect_descriptor_t engineDescriptor,
SpatializerPolicyCallback *callback)
: mEngineDescriptor(engineDescriptor), mPolicyCallback(callback) {
ALOGV("%s", __func__);
}
Spatializer::~Spatializer() {
ALOGV("%s", __func__);
}
status_t Spatializer::registerCallback(
const sp<media::INativeSpatializerCallback>& callback) {
Mutex::Autolock _l(mLock);
if (callback == nullptr) {
return BAD_VALUE;
}
sp<IBinder> binder = IInterface::asBinder(callback);
status_t status = binder->linkToDeath(this);
if (status == NO_ERROR) {
mSpatializerCallback = callback;
}
ALOGV("%s status %d", __func__, status);
return status;
}
// IBinder::DeathRecipient
void Spatializer::binderDied(__unused const wp<IBinder> &who) {
{
Mutex::Autolock _l(mLock);
mLevel = SpatializationLevel::NONE;
mSpatializerCallback.clear();
}
ALOGV("%s", __func__);
mPolicyCallback->onCheckSpatializer();
}
// ISpatializer
Status Spatializer::getSupportedLevels(std::vector<SpatializationLevel> *levels) {
ALOGV("%s", __func__);
if (levels == nullptr) {
return binderStatusFromStatusT(BAD_VALUE);
}
//TODO: get this from engine
levels->push_back(SpatializationLevel::NONE);
levels->push_back(SpatializationLevel::SPATIALIZER_MULTICHANNEL);
return Status::ok();
}
Status Spatializer::setLevel(media::SpatializationLevel level) {
ALOGV("%s level %d", __func__, (int)level);
if (level != SpatializationLevel::NONE
&& level != SpatializationLevel::SPATIALIZER_MULTICHANNEL) {
return binderStatusFromStatusT(BAD_VALUE);
}
sp<media::INativeSpatializerCallback> callback;
bool levelChanged = false;
{
Mutex::Autolock _l(mLock);
levelChanged = mLevel != level;
mLevel = level;
callback = mSpatializerCallback;
}
if (levelChanged) {
mPolicyCallback->onCheckSpatializer();
if (callback != nullptr) {
callback->onLevelChanged(level);
}
}
return Status::ok();
}
Status Spatializer::getLevel(media::SpatializationLevel *level) {
if (level == nullptr) {
return binderStatusFromStatusT(BAD_VALUE);
}
Mutex::Autolock _l(mLock);
*level = mLevel;
ALOGV("%s level %d", __func__, (int)*level);
return Status::ok();
}
Status Spatializer::getSupportedHeadTrackingModes(
std::vector<media::HeadTrackingMode>* modes) {
ALOGV("%s", __func__);
if (modes == nullptr) {
return binderStatusFromStatusT(BAD_VALUE);
}
//TODO: get this from:
// - The engine capabilities
// - If a head tracking sensor is registered and linked to a connected audio device
// - if we have indications on the screen orientation
modes->push_back(HeadTrackingMode::RELATIVE_WORLD);
return Status::ok();
}
Status Spatializer::setDesiredHeadTrackingMode(media::HeadTrackingMode mode) {
ALOGV("%s level %d", __func__, (int)mode);
if (mode != HeadTrackingMode::DISABLED
&& mode != HeadTrackingMode::RELATIVE_WORLD) {
return binderStatusFromStatusT(BAD_VALUE);
}
{
Mutex::Autolock _l(mLock);
mHeadTrackingMode = mode;
}
return Status::ok();
}
Status Spatializer::getActualHeadTrackingMode(media::HeadTrackingMode *mode) {
if (mode == nullptr) {
return binderStatusFromStatusT(BAD_VALUE);
}
Mutex::Autolock _l(mLock);
*mode = mHeadTrackingMode;
ALOGV("%s mode %d", __func__, (int)*mode);
return Status::ok();
}
Status Spatializer::recenterHeadtracker() {
return Status::ok();
}
Status Spatializer::setGlobalTransform(const std::vector<float>& screenToStage) {
Mutex::Autolock _l(mLock);
mScreenToStageTransform = screenToStage;
ALOGV("%s", __func__);
return Status::ok();
}
Status Spatializer::release() {
ALOGV("%s", __func__);
bool levelChanged = false;
{
Mutex::Autolock _l(mLock);
if (mSpatializerCallback == nullptr) {
return binderStatusFromStatusT(INVALID_OPERATION);
}
sp<IBinder> binder = IInterface::asBinder(mSpatializerCallback);
binder->unlinkToDeath(this);
mSpatializerCallback.clear();
levelChanged = mLevel != SpatializationLevel::NONE;
mLevel = SpatializationLevel::NONE;
}
if (levelChanged) {
mPolicyCallback->onCheckSpatializer();
}
return Status::ok();
}
status_t Spatializer::attachOutput(audio_io_handle_t output) {
Mutex::Autolock _l(mLock);
ALOGV("%s output %d mOutput %d", __func__, (int)output, (int)mOutput);
if (mOutput != AUDIO_IO_HANDLE_NONE) {
LOG_ALWAYS_FATAL_IF(mEngine != nullptr, "%s output set without FX engine", __func__);
// remove FX instance
mEngine->setEnabled(false);
mEngine.clear();
}
// create FX instance on output
AttributionSourceState attributionSource = AttributionSourceState();
mEngine = new AudioEffect(attributionSource);
mEngine->set(nullptr, &mEngineDescriptor.uuid, 0,
Spatializer::engineCallback /* cbf */, this /* user */,
AUDIO_SESSION_OUTPUT_STAGE, output,
{} /* device */, false /* probe */, true /* notifyFramesProcessed */);
status_t status = mEngine->initCheck();
ALOGV("%s mEngine create status %d", __func__, (int)status);
if (status != NO_ERROR) {
return status;
}
mEngine->setEnabled(true);
mOutput = output;
return NO_ERROR;
}
audio_io_handle_t Spatializer::detachOutput() {
Mutex::Autolock _l(mLock);
ALOGV("%s mOutput %d", __func__, (int)mOutput);
if (mOutput == AUDIO_IO_HANDLE_NONE) {
return AUDIO_IO_HANDLE_NONE;
}
// remove FX instance
mEngine->setEnabled(false);
mEngine.clear();
audio_io_handle_t output = mOutput;
mOutput = AUDIO_IO_HANDLE_NONE;
return output;
}
void Spatializer::engineCallback(int32_t event, void *user, void *info) {
if (user == nullptr) {
return;
}
const Spatializer * const me = reinterpret_cast<Spatializer *>(user);
switch (event) {
case AudioEffect::EVENT_FRAMES_PROCESSED: {
int frames = info == nullptr ? 0 : *(int *)info;
ALOGD("%s frames processed %d for me %p", __func__, frames, me);
} break;
default:
ALOGD("%s event %d", __func__, event);
break;
}
}
// ---------------------------------------------------------------------------
Spatializer::EffectClient::EffectClient(const sp<media::IEffectClient>& effectClient,
Spatializer& parent)
: BnEffect(),
mEffectClient(effectClient), mParent(parent) {
}
Spatializer::EffectClient::~EffectClient() {
}
// IEffect
#define RETURN(code) \
*_aidl_return = (code); \
return Status::ok();
// Write a POD value into a vector of bytes (clears the previous buffer
// content).
template<typename T>
void writeToBuffer(const T& value, std::vector<uint8_t>* buffer) {
buffer->clear();
appendToBuffer(value, buffer);
}
Status Spatializer::EffectClient::enable(int32_t* _aidl_return) {
RETURN(OK);
}
Status Spatializer::EffectClient::disable(int32_t* _aidl_return) {
RETURN(OK);
}
Status Spatializer::EffectClient::command(int32_t cmdCode,
const std::vector<uint8_t>& cmdData __unused,
int32_t maxResponseSize __unused,
std::vector<uint8_t>* response __unused,
int32_t* _aidl_return) {
// reject commands reserved for internal use by audio framework if coming from outside
// of audioserver
switch(cmdCode) {
case EFFECT_CMD_ENABLE:
case EFFECT_CMD_DISABLE:
case EFFECT_CMD_SET_PARAM_DEFERRED:
case EFFECT_CMD_SET_PARAM_COMMIT:
RETURN(BAD_VALUE);
case EFFECT_CMD_SET_PARAM:
case EFFECT_CMD_GET_PARAM:
break;
default:
if (cmdCode >= EFFECT_CMD_FIRST_PROPRIETARY) {
break;
}
android_errorWriteLog(0x534e4554, "62019992");
RETURN(BAD_VALUE);
}
(void)mParent;
RETURN(OK);
}
Status Spatializer::EffectClient::disconnect() {
mDisconnected = true;
return Status::ok();
}
Status Spatializer::EffectClient::getCblk(media::SharedFileRegion* _aidl_return) {
LOG_ALWAYS_FATAL_IF(!convertIMemoryToSharedFileRegion(mCblkMemory, _aidl_return));
return Status::ok();
}
} // namespace android