1 /*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "AHAL_DownmixContext"
18
19 #include <android-base/logging.h>
20
21 #include "DownmixContext.h"
22
23 using aidl::android::hardware::audio::effect::IEffect;
24 using aidl::android::hardware::audio::common::getChannelCount;
25 using aidl::android::media::audio::common::AudioChannelLayout;
26
27 namespace aidl::android::hardware::audio::effect {
28
DownmixContext(int statusDepth,const Parameter::Common & common)29 DownmixContext::DownmixContext(int statusDepth, const Parameter::Common& common)
30 : EffectContext(statusDepth, common) {
31 LOG(DEBUG) << __func__;
32 mState = DOWNMIX_STATE_UNINITIALIZED;
33 init_params(common);
34 }
35
~DownmixContext()36 DownmixContext::~DownmixContext() {
37 LOG(DEBUG) << __func__;
38 mState = DOWNMIX_STATE_UNINITIALIZED;
39 }
40
enable()41 RetCode DownmixContext::enable() {
42 LOG(DEBUG) << __func__;
43 if (mState != DOWNMIX_STATE_INITIALIZED) {
44 return RetCode::ERROR_EFFECT_LIB_ERROR;
45 }
46 mState = DOWNMIX_STATE_ACTIVE;
47 return RetCode::SUCCESS;
48 }
49
disable()50 RetCode DownmixContext::disable() {
51 LOG(DEBUG) << __func__;
52 if (mState != DOWNMIX_STATE_ACTIVE) {
53 return RetCode::ERROR_EFFECT_LIB_ERROR;
54 }
55 mState = DOWNMIX_STATE_INITIALIZED;
56 return RetCode::SUCCESS;
57 }
58
reset()59 void DownmixContext::reset() {
60 LOG(DEBUG) << __func__;
61 disable();
62 resetBuffer();
63 }
64
lvmProcess(float * in,float * out,int samples)65 IEffect::Status DownmixContext::lvmProcess(float* in, float* out, int samples) {
66 LOG(DEBUG) << __func__ << " in " << in << " out " << out << " sample " << samples;
67 IEffect::Status status = {EX_ILLEGAL_ARGUMENT, 0, 0};
68
69 if (in == nullptr || out == nullptr || getInputFrameSize() != getOutputFrameSize() ||
70 getInputFrameSize() == 0) {
71 return status;
72 }
73
74 status = {EX_ILLEGAL_STATE, 0, 0};
75 if (mState == DOWNMIX_STATE_UNINITIALIZED) {
76 LOG(ERROR) << __func__ << "Trying to use an uninitialized downmixer";
77 return status;
78 } else if (mState == DOWNMIX_STATE_INITIALIZED) {
79 LOG(ERROR) << __func__ << "Trying to use a non-configured downmixer";
80 return status;
81 }
82
83 LOG(DEBUG) << __func__ << " start processing";
84 bool accumulate = false;
85 int frames = samples * sizeof(float) / getInputFrameSize();
86 if (mType == Downmix::Type::STRIP) {
87 int inputChannelCount = getChannelCount(mChMask);
88 while (frames) {
89 if (accumulate) {
90 out[0] = std::clamp(out[0] + in[0], -1.f, 1.f);
91 out[1] = std::clamp(out[1] + in[1], -1.f, 1.f);
92 } else {
93 out[0] = in[0];
94 out[1] = in[1];
95 }
96 in += inputChannelCount;
97 out += 2;
98 frames--;
99 }
100 } else {
101 int chMask = mChMask.get<AudioChannelLayout::layoutMask>();
102 if (!mChannelMix.process(in, out, frames, accumulate, (audio_channel_mask_t)chMask)) {
103 LOG(ERROR) << "Multichannel configuration " << mChMask.toString()
104 << " is not supported";
105 return status;
106 }
107 }
108 LOG(DEBUG) << __func__ << " done processing";
109 return {STATUS_OK, samples, samples};
110 }
111
init_params(const Parameter::Common & common)112 void DownmixContext::init_params(const Parameter::Common& common) {
113 // when configuring the effect, do not allow a blank or unsupported channel mask
114 AudioChannelLayout channelMask = common.input.base.channelMask;
115 if (isChannelMaskValid(channelMask)) {
116 LOG(ERROR) << "Downmix_Configure error: input channel mask " << channelMask.toString()
117 << " not supported";
118 } else {
119 mType = Downmix::Type::FOLD;
120 mChMask = channelMask;
121 mState = DOWNMIX_STATE_INITIALIZED;
122 }
123 }
124
isChannelMaskValid(AudioChannelLayout channelMask)125 bool DownmixContext::isChannelMaskValid(AudioChannelLayout channelMask) {
126 if (channelMask.getTag() == AudioChannelLayout::layoutMask) return false;
127 int chMask = channelMask.get<AudioChannelLayout::layoutMask>();
128 // check against unsupported channels (up to FCC_26)
129 constexpr uint32_t MAXIMUM_CHANNEL_MASK = AudioChannelLayout::LAYOUT_22POINT2 |
130 AudioChannelLayout::CHANNEL_FRONT_WIDE_LEFT |
131 AudioChannelLayout::CHANNEL_FRONT_WIDE_RIGHT;
132 if (chMask & ~MAXIMUM_CHANNEL_MASK) {
133 LOG(ERROR) << "Unsupported channels in " << (chMask & ~MAXIMUM_CHANNEL_MASK);
134 return false;
135 }
136 return true;
137 }
138
139 } // namespace aidl::android::hardware::audio::effect
140