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 #pragma once
18 
19 #include <algorithm>
20 #include <memory>
21 #include <optional>
22 #include <string>
23 #include <type_traits>
24 #include <unordered_map>
25 #include <utility>
26 #include <vector>
27 
28 #include <Utils.h>
29 #include <aidl/android/hardware/audio/effect/IEffect.h>
30 #include <aidl/android/hardware/audio/effect/IFactory.h>
31 #include <aidl/android/media/audio/common/AudioChannelLayout.h>
32 #include <android/binder_auto_utils.h>
33 #include <fmq/AidlMessageQueue.h>
34 #include <gtest/gtest.h>
35 #include <system/audio_aidl_utils.h>
36 #include <system/audio_effects/aidl_effects_utils.h>
37 #include <system/audio_effects/effect_uuid.h>
38 
39 #include "EffectFactoryHelper.h"
40 #include "TestUtils.h"
41 #include "pffft.hpp"
42 
43 using namespace android;
44 using aidl::android::hardware::audio::effect::CommandId;
45 using aidl::android::hardware::audio::effect::Descriptor;
46 using aidl::android::hardware::audio::effect::getEffectTypeUuidSpatializer;
47 using aidl::android::hardware::audio::effect::getRange;
48 using aidl::android::hardware::audio::effect::IEffect;
49 using aidl::android::hardware::audio::effect::isRangeValid;
50 using aidl::android::hardware::audio::effect::kEffectTypeUuidSpatializer;
51 using aidl::android::hardware::audio::effect::kEventFlagDataMqNotEmpty;
52 using aidl::android::hardware::audio::effect::kEventFlagDataMqUpdate;
53 using aidl::android::hardware::audio::effect::kEventFlagNotEmpty;
54 using aidl::android::hardware::audio::effect::kReopenSupportedVersion;
55 using aidl::android::hardware::audio::effect::Parameter;
56 using aidl::android::hardware::audio::effect::Range;
57 using aidl::android::hardware::audio::effect::Spatializer;
58 using aidl::android::hardware::audio::effect::State;
59 using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
60 using aidl::android::media::audio::common::AudioChannelLayout;
61 using aidl::android::media::audio::common::AudioFormatDescription;
62 using aidl::android::media::audio::common::AudioFormatType;
63 using aidl::android::media::audio::common::AudioUuid;
64 using aidl::android::media::audio::common::PcmType;
65 using ::android::audio::utils::toString;
66 using ::android::hardware::EventFlag;
67 
68 const AudioFormatDescription kDefaultFormatDescription = {
69         .type = AudioFormatType::PCM, .pcm = PcmType::FLOAT_32_BIT, .encoding = ""};
70 
71 typedef ::android::AidlMessageQueue<IEffect::Status,
72                                     ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
73         StatusMQ;
74 typedef ::android::AidlMessageQueue<float,
75                                     ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
76         DataMQ;
77 
getPrefix(Descriptor & descriptor)78 static inline std::string getPrefix(Descriptor& descriptor) {
79     std::string prefix = "Implementor_" + descriptor.common.implementor + "_name_" +
80                          descriptor.common.name + "_UUID_" + toString(descriptor.common.id.uuid);
81     std::replace_if(
82             prefix.begin(), prefix.end(), [](const char c) { return !std::isalnum(c); }, '_');
83     return prefix;
84 }
85 
86 class EffectHelper {
87   public:
88     void create(std::shared_ptr<IFactory> factory, std::shared_ptr<IEffect>& effect,
89                 Descriptor& desc, binder_status_t status = EX_NONE) {
90         ASSERT_NE(factory, nullptr);
91         auto& id = desc.common.id;
92         ASSERT_STATUS(status, factory->createEffect(id.uuid, &effect));
93         if (status == EX_NONE) {
94             ASSERT_NE(effect, nullptr) << toString(id.uuid);
95         }
96         mIsSpatializer = id.type == getEffectTypeUuidSpatializer();
97         mDescriptor = desc;
98     }
99 
destroyIgnoreRet(std::shared_ptr<IFactory> factory,std::shared_ptr<IEffect> effect)100     static void destroyIgnoreRet(std::shared_ptr<IFactory> factory,
101                                  std::shared_ptr<IEffect> effect) {
102         if (factory && effect) {
103             factory->destroyEffect(effect);
104         }
105     }
106 
107     static void destroy(std::shared_ptr<IFactory> factory, std::shared_ptr<IEffect> effect,
108                         binder_status_t status = EX_NONE) {
109         ASSERT_NE(factory, nullptr);
110         ASSERT_NE(effect, nullptr);
111         ASSERT_STATUS(status, factory->destroyEffect(effect));
112     }
113 
114     static void open(std::shared_ptr<IEffect> effect, const Parameter::Common& common,
115                      const std::optional<Parameter::Specific>& specific,
116                      IEffect::OpenEffectReturn* ret, binder_status_t status = EX_NONE) {
117         ASSERT_NE(effect, nullptr);
118         ASSERT_STATUS(status, effect->open(common, specific, ret));
119     }
120 
121     void open(std::shared_ptr<IEffect> effect, int session = 0, binder_status_t status = EX_NONE) {
122         ASSERT_NE(effect, nullptr);
123         Parameter::Common common = createParamCommon(session);
124         IEffect::OpenEffectReturn ret;
125         ASSERT_NO_FATAL_FAILURE(open(effect, common, std::nullopt /* specific */, &ret, status));
126     }
127 
closeIgnoreRet(std::shared_ptr<IEffect> effect)128     static void closeIgnoreRet(std::shared_ptr<IEffect> effect) {
129         if (effect) {
130             effect->close();
131         }
132     }
133     static void close(std::shared_ptr<IEffect> effect, binder_status_t status = EX_NONE) {
134         if (effect) {
135             ASSERT_STATUS(status, effect->close());
136         }
137     }
138     static void getDescriptor(std::shared_ptr<IEffect> effect, Descriptor& desc,
139                               binder_status_t status = EX_NONE) {
140         ASSERT_NE(effect, nullptr);
141         ASSERT_STATUS(status, effect->getDescriptor(&desc));
142     }
143     static void expectState(std::shared_ptr<IEffect> effect, State expectState,
144                             binder_status_t status = EX_NONE) {
145         ASSERT_NE(effect, nullptr);
146         State state;
147         ASSERT_STATUS(status, effect->getState(&state));
148         ASSERT_EQ(expectState, state);
149     }
commandIgnoreRet(std::shared_ptr<IEffect> effect,CommandId command)150     static void commandIgnoreRet(std::shared_ptr<IEffect> effect, CommandId command) {
151         if (effect) {
152             effect->command(command);
153         }
154     }
155     static void command(std::shared_ptr<IEffect> effect, CommandId command,
156                         binder_status_t status = EX_NONE) {
157         ASSERT_NE(effect, nullptr);
158         ASSERT_STATUS(status, effect->command(command));
159     }
allocateInputData(const Parameter::Common common,std::unique_ptr<DataMQ> & mq,std::vector<float> & buffer)160     static void allocateInputData(const Parameter::Common common, std::unique_ptr<DataMQ>& mq,
161                                   std::vector<float>& buffer) {
162         ASSERT_NE(mq, nullptr);
163         auto frameSize = ::aidl::android::hardware::audio::common::getFrameSizeInBytes(
164                 common.input.base.format, common.input.base.channelMask);
165         const size_t floatsToWrite = mq->availableToWrite();
166         ASSERT_NE(0UL, floatsToWrite);
167         ASSERT_EQ(frameSize * common.input.frameCount, floatsToWrite * sizeof(float));
168         buffer.resize(floatsToWrite);
169         std::fill(buffer.begin(), buffer.end(), 0x5a);
170     }
writeToFmq(std::unique_ptr<StatusMQ> & statusMq,std::unique_ptr<DataMQ> & dataMq,const std::vector<float> & buffer,int version)171     static void writeToFmq(std::unique_ptr<StatusMQ>& statusMq, std::unique_ptr<DataMQ>& dataMq,
172                            const std::vector<float>& buffer, int version) {
173         const size_t available = dataMq->availableToWrite();
174         ASSERT_NE(0Ul, available);
175         auto bufferFloats = buffer.size();
176         auto floatsToWrite = std::min(available, bufferFloats);
177         ASSERT_TRUE(dataMq->write(buffer.data(), floatsToWrite));
178 
179         EventFlag* efGroup;
180         ASSERT_EQ(::android::OK,
181                   EventFlag::createEventFlag(statusMq->getEventFlagWord(), &efGroup));
182         ASSERT_NE(nullptr, efGroup);
183         efGroup->wake(version >= kReopenSupportedVersion ? kEventFlagDataMqNotEmpty
184                                                          : kEventFlagNotEmpty);
185         ASSERT_EQ(::android::OK, EventFlag::deleteEventFlag(&efGroup));
186     }
187     static void readFromFmq(std::unique_ptr<StatusMQ>& statusMq, size_t statusNum,
188                             std::unique_ptr<DataMQ>& dataMq, size_t expectFloats,
189                             std::vector<float>& buffer,
190                             std::optional<int> expectStatus = STATUS_OK) {
191         if (0 == statusNum) {
192             ASSERT_EQ(0ul, statusMq->availableToRead());
193             return;
194         }
195         IEffect::Status status{};
196         ASSERT_TRUE(statusMq->readBlocking(&status, statusNum));
197         if (expectStatus.has_value()) {
198             ASSERT_EQ(expectStatus.value(), status.status);
199         }
200 
201         ASSERT_EQ(expectFloats, (unsigned)status.fmqProduced);
202         ASSERT_EQ(expectFloats, dataMq->availableToRead());
203         if (expectFloats != 0) {
204             ASSERT_TRUE(dataMq->read(buffer.data(), expectFloats));
205         }
206     }
expectDataMqUpdateEventFlag(std::unique_ptr<StatusMQ> & statusMq)207     static void expectDataMqUpdateEventFlag(std::unique_ptr<StatusMQ>& statusMq) {
208         EventFlag* efGroup;
209         ASSERT_EQ(::android::OK,
210                   EventFlag::createEventFlag(statusMq->getEventFlagWord(), &efGroup));
211         ASSERT_NE(nullptr, efGroup);
212         uint32_t efState = 0;
213         EXPECT_EQ(::android::OK, efGroup->wait(kEventFlagDataMqUpdate, &efState, 1'000'000 /*1ms*/,
214                                                true /* retry */));
215         EXPECT_TRUE(efState & kEventFlagDataMqUpdate);
216     }
217 
218     Parameter::Common createParamCommon(int session = 0, int ioHandle = -1, int iSampleRate = 48000,
219                                         int oSampleRate = 48000, long iFrameCount = 0x100,
220                                         long oFrameCount = 0x100) {
221         AudioChannelLayout defaultLayout = AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
222                 AudioChannelLayout::LAYOUT_STEREO);
223         // query supported input layout and use it as the default parameter in common
224         if (mIsSpatializer && isRangeValid<Range::spatializer>(Spatializer::supportedChannelLayout,
225                                                                mDescriptor.capability)) {
226             const auto layoutRange = getRange<Range::spatializer, Range::SpatializerRange>(
227                     mDescriptor.capability, Spatializer::supportedChannelLayout);
228             if (std::vector<AudioChannelLayout> layouts;
229                 layoutRange &&
230                 0 != (layouts = layoutRange->min.get<Spatializer::supportedChannelLayout>())
231                                 .size()) {
232                 defaultLayout = layouts[0];
233             }
234         }
235         return createParamCommon(session, ioHandle, iSampleRate, oSampleRate, iFrameCount,
236                                  oFrameCount, defaultLayout, defaultLayout);
237     }
createParamCommon(int session,int ioHandle,int iSampleRate,int oSampleRate,long iFrameCount,long oFrameCount,AudioChannelLayout inputChannelLayout,AudioChannelLayout outputChannelLayout)238     static Parameter::Common createParamCommon(int session, int ioHandle, int iSampleRate,
239                                                int oSampleRate, long iFrameCount, long oFrameCount,
240                                                AudioChannelLayout inputChannelLayout,
241                                                AudioChannelLayout outputChannelLayout) {
242         Parameter::Common common;
243         common.session = session;
244         common.ioHandle = ioHandle;
245 
246         auto& input = common.input;
247         auto& output = common.output;
248         input.base.sampleRate = iSampleRate;
249         input.base.channelMask = inputChannelLayout;
250         input.base.format = kDefaultFormatDescription;
251         input.frameCount = iFrameCount;
252         output.base.sampleRate = oSampleRate;
253         output.base.channelMask = outputChannelLayout;
254         output.base.format = kDefaultFormatDescription;
255         output.frameCount = oFrameCount;
256         return common;
257     }
258 
259     typedef ::android::AidlMessageQueue<
260             IEffect::Status, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
261             StatusMQ;
262     typedef ::android::AidlMessageQueue<
263             float, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
264             DataMQ;
265 
266     class EffectParam {
267       public:
268         std::unique_ptr<StatusMQ> statusMQ;
269         std::unique_ptr<DataMQ> inputMQ;
270         std::unique_ptr<DataMQ> outputMQ;
271     };
272 
273     template <typename T, Range::Tag tag>
isParameterValid(const T & target,const Descriptor & desc)274     static bool isParameterValid(const T& target, const Descriptor& desc) {
275         if (desc.capability.range.getTag() != tag) {
276             return true;
277         }
278         const auto& ranges = desc.capability.range.get<tag>();
279         return inRange(target, ranges);
280     }
281 
282     /**
283      * Add to test value set: (min+max)/2, minimum/maximum numeric limits, and min-1/max+1 if
284      * result still in numeric limits after -1/+1.
285      * Only use this when the type of test value is basic type (std::is_arithmetic return true).
286      */
287     template <typename S, typename = std::enable_if_t<std::is_arithmetic_v<S>>>
expandTestValueBasic(std::set<S> & s)288     static std::set<S> expandTestValueBasic(std::set<S>& s) {
289         const auto minLimit = std::numeric_limits<S>::min(),
290                    maxLimit = std::numeric_limits<S>::max();
291         if (s.size()) {
292             const auto min = *s.begin(), max = *s.rbegin();
293             s.insert((min & max) + ((min ^ max) >> 1));
294             if (min > minLimit + 1) {
295                 s.insert(min - 1);
296             }
297             if (max < maxLimit - 1) {
298                 s.insert(max + 1);
299             }
300         }
301         s.insert(minLimit);
302         s.insert(maxLimit);
303         return s;
304     }
305 
306     template <typename T, typename S, Range::Tag R, typename T::Tag tag>
getTestValueSet(std::vector<std::pair<std::shared_ptr<IFactory>,Descriptor>> descList)307     static std::set<S> getTestValueSet(
308             std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> descList) {
309         std::set<S> result;
310         for (const auto& [_, desc] : descList) {
311             if (desc.capability.range.getTag() == R) {
312                 const auto& ranges = desc.capability.range.get<R>();
313                 for (const auto& range : ranges) {
314                     if (range.min.getTag() == tag) {
315                         result.insert(range.min.template get<tag>());
316                     }
317                     if (range.max.getTag() == tag) {
318                         result.insert(range.max.template get<tag>());
319                     }
320                 }
321             }
322         }
323         return result;
324     }
325 
326     template <typename T, typename S, Range::Tag R, typename T::Tag tag, typename Functor>
getTestValueSet(std::vector<std::pair<std::shared_ptr<IFactory>,Descriptor>> descList,Functor functor)327     static std::set<S> getTestValueSet(
328             std::vector<std::pair<std::shared_ptr<IFactory>, Descriptor>> descList,
329             Functor functor) {
330         auto result = getTestValueSet<T, S, R, tag>(descList);
331         return functor(result);
332     }
333 
processAndWriteToOutput(std::vector<float> & inputBuffer,std::vector<float> & outputBuffer,const std::shared_ptr<IEffect> & mEffect,IEffect::OpenEffectReturn * mOpenEffectReturn)334     static void processAndWriteToOutput(std::vector<float>& inputBuffer,
335                                         std::vector<float>& outputBuffer,
336                                         const std::shared_ptr<IEffect>& mEffect,
337                                         IEffect::OpenEffectReturn* mOpenEffectReturn) {
338         // Initialize AidlMessagequeues
339         auto statusMQ = std::make_unique<EffectHelper::StatusMQ>(mOpenEffectReturn->statusMQ);
340         ASSERT_TRUE(statusMQ->isValid());
341         auto inputMQ = std::make_unique<EffectHelper::DataMQ>(mOpenEffectReturn->inputDataMQ);
342         ASSERT_TRUE(inputMQ->isValid());
343         auto outputMQ = std::make_unique<EffectHelper::DataMQ>(mOpenEffectReturn->outputDataMQ);
344         ASSERT_TRUE(outputMQ->isValid());
345 
346         // Enabling the process
347         ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::START));
348         ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::PROCESSING));
349 
350         // Write from buffer to message queues and calling process
351         EXPECT_NO_FATAL_FAILURE(EffectHelper::writeToFmq(statusMQ, inputMQ, inputBuffer, [&]() {
352             int version = 0;
353             return (mEffect && mEffect->getInterfaceVersion(&version).isOk()) ? version : 0;
354         }()));
355 
356         // Read the updated message queues into buffer
357         EXPECT_NO_FATAL_FAILURE(EffectHelper::readFromFmq(statusMQ, 1, outputMQ,
358                                                           outputBuffer.size(), outputBuffer));
359 
360         // Disable the process
361         ASSERT_NO_FATAL_FAILURE(command(mEffect, CommandId::RESET));
362         ASSERT_NO_FATAL_FAILURE(expectState(mEffect, State::IDLE));
363     }
364 
365     // Find FFT bin indices for testFrequencies and get bin center frequencies
roundToFreqCenteredToFftBin(std::vector<int> & testFrequencies,std::vector<int> & binOffsets,const float kBinWidth)366     void roundToFreqCenteredToFftBin(std::vector<int>& testFrequencies,
367                                      std::vector<int>& binOffsets, const float kBinWidth) {
368         for (size_t i = 0; i < testFrequencies.size(); i++) {
369             binOffsets[i] = std::round(testFrequencies[i] / kBinWidth);
370             testFrequencies[i] = std::round(binOffsets[i] * kBinWidth);
371         }
372     }
373 
374     // Generate multitone input between -1 to +1 using testFrequencies
generateMultiTone(const std::vector<int> & testFrequencies,std::vector<float> & input,const int samplingFrequency)375     void generateMultiTone(const std::vector<int>& testFrequencies, std::vector<float>& input,
376                            const int samplingFrequency) {
377         for (size_t i = 0; i < input.size(); i++) {
378             input[i] = 0;
379 
380             for (size_t j = 0; j < testFrequencies.size(); j++) {
381                 input[i] += sin(2 * M_PI * testFrequencies[j] * i / samplingFrequency);
382             }
383             input[i] /= testFrequencies.size();
384         }
385     }
386 
387     // Use FFT transform to convert the buffer to frequency domain
388     // Compute its magnitude at binOffsets
calculateMagnitude(const std::vector<float> & buffer,const std::vector<int> & binOffsets,const int nPointFFT)389     std::vector<float> calculateMagnitude(const std::vector<float>& buffer,
390                                           const std::vector<int>& binOffsets, const int nPointFFT) {
391         std::vector<float> fftInput(nPointFFT);
392         PFFFT_Setup* inputHandle = pffft_new_setup(nPointFFT, PFFFT_REAL);
393         pffft_transform_ordered(inputHandle, buffer.data(), fftInput.data(), nullptr,
394                                 PFFFT_FORWARD);
395         pffft_destroy_setup(inputHandle);
396         std::vector<float> bufferMag(binOffsets.size());
397         for (size_t i = 0; i < binOffsets.size(); i++) {
398             size_t k = binOffsets[i];
399             bufferMag[i] = sqrt((fftInput[k * 2] * fftInput[k * 2]) +
400                                 (fftInput[k * 2 + 1] * fftInput[k * 2 + 1]));
401         }
402 
403         return bufferMag;
404     }
405 
406     bool mIsSpatializer;
407     Descriptor mDescriptor;
408 };
409