1 /*
2  * Copyright 2020 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 #include <array>
18 #include <climits>
19 #include <cstdlib>
20 #include <random>
21 #include <vector>
22 #include <log/log.h>
23 #include <benchmark/benchmark.h>
24 #include <hardware/audio_effect.h>
25 #include <system/audio.h>
26 #include "EffectReverb.h"
27 
28 extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
29 constexpr effect_uuid_t kEffectUuids[] = {
30         {0x172cdf00,
31          0xa3bc,
32          0x11df,
33          0xa72f,
34          {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},  // preset-insert mode
35         {0xf29a1400,
36          0xa3bb,
37          0x11df,
38          0x8ddc,
39          {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},  // preset-aux mode
40 };
41 
42 constexpr size_t kNumEffectUuids = std::size(kEffectUuids);
43 
44 constexpr size_t kFrameCount = 2048;
45 
46 constexpr int kPresets[] = {
47         REVERB_PRESET_NONE,      REVERB_PRESET_SMALLROOM,  REVERB_PRESET_MEDIUMROOM,
48         REVERB_PRESET_LARGEROOM, REVERB_PRESET_MEDIUMHALL, REVERB_PRESET_LARGEHALL,
49         REVERB_PRESET_PLATE,
50 };
51 
52 constexpr size_t kNumPresets = std::size(kPresets);
53 
54 constexpr int kSampleRate = 44100;
55 
reverbSetConfigParam(uint32_t paramType,uint32_t paramValue,effect_handle_t effectHandle)56 int reverbSetConfigParam(uint32_t paramType, uint32_t paramValue, effect_handle_t effectHandle) {
57     int reply = 0;
58     uint32_t replySize = sizeof(reply);
59     uint32_t paramData[2] = {paramType, paramValue};
60     auto effectParam = (effect_param_t*)malloc(sizeof(effect_param_t) + sizeof(paramData));
61     memcpy(&effectParam->data[0], &paramData[0], sizeof(paramData));
62     effectParam->psize = sizeof(paramData[0]);
63     effectParam->vsize = sizeof(paramData[1]);
64     int status = (*effectHandle)
65                          ->command(effectHandle, EFFECT_CMD_SET_PARAM,
66                                    sizeof(effect_param_t) + sizeof(paramData), effectParam,
67                                    &replySize, &reply);
68     free(effectParam);
69     if (status != 0) {
70         ALOGE("Reverb set config returned an error = %d\n", status);
71         return status;
72     }
73     return reply;
74 }
75 
76 /*******************************************************************
77  * A test result running on Pixel 3 with for comparison.
78  * The first parameter indicates the preset level id.
79  * The second parameter indicates the effect.
80  * 0: preset-insert mode, 1: preset-aux mode
81  * --------------------------------------------------------
82  * Benchmark              Time             CPU   Iterations
83  * --------------------------------------------------------
84  * BM_REVERB/0/0      19312 ns        19249 ns        36282
85  * BM_REVERB/0/1       5613 ns         5596 ns       125032
86  * BM_REVERB/1/0     605453 ns       603714 ns         1131
87  * BM_REVERB/1/1     589421 ns       587758 ns         1161
88  * BM_REVERB/2/0     605760 ns       604006 ns         1131
89  * BM_REVERB/2/1     589434 ns       587777 ns         1161
90  * BM_REVERB/3/0     605574 ns       603828 ns         1131
91  * BM_REVERB/3/1     589566 ns       587862 ns         1162
92  * BM_REVERB/4/0     605634 ns       603894 ns         1131
93  * BM_REVERB/4/1     589506 ns       587856 ns         1161
94  * BM_REVERB/5/0     605644 ns       603929 ns         1131
95  * BM_REVERB/5/1     589592 ns       587863 ns         1161
96  * BM_REVERB/6/0     610544 ns       608561 ns         1131
97  * BM_REVERB/6/1     589686 ns       587871 ns         1161
98  *******************************************************************/
99 
BM_REVERB(benchmark::State & state)100 static void BM_REVERB(benchmark::State& state) {
101     const size_t chMask = AUDIO_CHANNEL_OUT_STEREO;
102     const size_t preset = kPresets[state.range(0)];
103     const effect_uuid_t uuid = kEffectUuids[state.range(1)];
104     const size_t channelCount = audio_channel_count_from_out_mask(chMask);
105 
106     // Initialize input buffer with deterministic pseudo-random values
107     std::minstd_rand gen(chMask);
108     std::uniform_real_distribution<> dis(-1.0f, 1.0f);
109     std::vector<float> input(kFrameCount * channelCount);
110     std::vector<float> output(kFrameCount * channelCount);
111     for (auto& in : input) {
112         in = dis(gen);
113     }
114 
115     effect_handle_t effectHandle = nullptr;
116     if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(&uuid, 1, 1, &effectHandle);
117         status != 0) {
118         ALOGE("create_effect returned an error = %d\n", status);
119         return;
120     }
121 
122     effect_config_t config{};
123     config.inputCfg.samplingRate = config.outputCfg.samplingRate = kSampleRate;
124     config.inputCfg.channels = config.outputCfg.channels = chMask;
125     config.inputCfg.format = config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
126 
127     int reply = 0;
128     uint32_t replySize = sizeof(reply);
129     if (int status = (*effectHandle)
130                              ->command(effectHandle, EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t),
131                                        &config, &replySize, &reply);
132         status != 0) {
133         ALOGE("command returned an error = %d\n", status);
134         return;
135     }
136 
137     if (int status =
138                 (*effectHandle)
139                         ->command(effectHandle, EFFECT_CMD_ENABLE, 0, nullptr, &replySize, &reply);
140         status != 0) {
141         ALOGE("Command enable call returned error %d\n", reply);
142         return;
143     }
144 
145     if (int status = reverbSetConfigParam(REVERB_PARAM_PRESET, preset, effectHandle); status != 0) {
146         ALOGE("Invalid reverb preset. Error %d\n", status);
147         return;
148     }
149 
150     // Run the test
151     for (auto _ : state) {
152         benchmark::DoNotOptimize(input.data());
153         benchmark::DoNotOptimize(output.data());
154 
155         audio_buffer_t inBuffer = {.frameCount = kFrameCount, .f32 = input.data()};
156         audio_buffer_t outBuffer = {.frameCount = kFrameCount, .f32 = output.data()};
157         (*effectHandle)->process(effectHandle, &inBuffer, &outBuffer);
158 
159         benchmark::ClobberMemory();
160     }
161 
162     state.SetComplexityN(state.range(0));
163 
164     if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(effectHandle); status != 0) {
165         ALOGE("release_effect returned an error = %d\n", status);
166         return;
167     }
168 }
169 
REVERBArgs(benchmark::internal::Benchmark * b)170 static void REVERBArgs(benchmark::internal::Benchmark* b) {
171     for (int i = 0; i < kNumPresets; i++) {
172         for (int j = 0; j < kNumEffectUuids; ++j) {
173             b->Args({i, j});
174         }
175     }
176 }
177 
178 BENCHMARK(BM_REVERB)->Apply(REVERBArgs);
179 
180 BENCHMARK_MAIN();
181