1 /*
2  * Copyright 2021 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 <random>
18 #include <vector>
19 
20 #include <audio_effects/effect_downmix.h>
21 #include <audio_utils/channels.h>
22 #include <audio_utils/primitives.h>
23 #include <audio_utils/Statistics.h>
24 #include <benchmark/benchmark.h>
25 #include <log/log.h>
26 #include <system/audio.h>
27 
28 #include "EffectDownmix.h"
29 
30 extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
31 
32 static constexpr audio_channel_mask_t kChannelPositionMasks[] = {
33     AUDIO_CHANNEL_OUT_FRONT_LEFT,
34     AUDIO_CHANNEL_OUT_FRONT_CENTER,
35     AUDIO_CHANNEL_OUT_STEREO,
36     AUDIO_CHANNEL_OUT_2POINT1,
37     AUDIO_CHANNEL_OUT_2POINT0POINT2,
38     AUDIO_CHANNEL_OUT_QUAD, // AUDIO_CHANNEL_OUT_QUAD_BACK
39     AUDIO_CHANNEL_OUT_QUAD_SIDE,
40     AUDIO_CHANNEL_OUT_SURROUND,
41     AUDIO_CHANNEL_OUT_2POINT1POINT2,
42     AUDIO_CHANNEL_OUT_3POINT0POINT2,
43     AUDIO_CHANNEL_OUT_PENTA,
44     AUDIO_CHANNEL_OUT_3POINT1POINT2,
45     AUDIO_CHANNEL_OUT_5POINT1, // AUDIO_CHANNEL_OUT_5POINT1_BACK
46     AUDIO_CHANNEL_OUT_5POINT1_SIDE,
47     AUDIO_CHANNEL_OUT_6POINT1,
48     AUDIO_CHANNEL_OUT_5POINT1POINT2,
49     AUDIO_CHANNEL_OUT_7POINT1,
50     AUDIO_CHANNEL_OUT_5POINT1POINT4,
51     AUDIO_CHANNEL_OUT_7POINT1POINT2,
52     AUDIO_CHANNEL_OUT_7POINT1POINT4,
53     AUDIO_CHANNEL_OUT_13POINT_360RA,
54     AUDIO_CHANNEL_OUT_22POINT2,
55 };
56 
57 static constexpr effect_uuid_t downmix_uuid = {
58     0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}};
59 
60 static constexpr size_t kFrameCount = 1000;
61 
62 /*
63 Pixel 7
64 $ atest downmix_benchmark
65 
66 --------------------------------------------------------
67 Benchmark              Time             CPU   Iterations
68 --------------------------------------------------------
69 downmix_benchmark:
70   #BM_Downmix/0     2216 ns    2208 ns       308323
71   #BM_Downmix/1     2237 ns    2228 ns       314730
72   #BM_Downmix/2      270 ns     268 ns      2681469
73   #BM_Downmix/3     3016 ns    2999 ns       234146
74   #BM_Downmix/4     3331 ns    3313 ns       212026
75   #BM_Downmix/5      816 ns     809 ns       864395
76   #BM_Downmix/6      813 ns     809 ns       863876
77   #BM_Downmix/7     3336 ns    3319 ns       211938
78   #BM_Downmix/8     3786 ns    3762 ns       185047
79   #BM_Downmix/9     3810 ns    3797 ns       186840
80   #BM_Downmix/10    3767 ns    3746 ns       187015
81   #BM_Downmix/11    4212 ns    4191 ns       166119
82   #BM_Downmix/12    1245 ns    1231 ns       574388
83   #BM_Downmix/13    1234 ns    1228 ns       574743
84   #BM_Downmix/14    4795 ns    4771 ns       147157
85   #BM_Downmix/15    1334 ns    1327 ns       527728
86   #BM_Downmix/16    1346 ns    1332 ns       525444
87   #BM_Downmix/17    2144 ns    2121 ns       333343
88   #BM_Downmix/18    2133 ns    2118 ns       330391
89   #BM_Downmix/19    2527 ns    2513 ns       278553
90   #BM_Downmix/20    8148 ns    8113 ns        86136
91   #BM_Downmix/21    6332 ns    6301 ns       111134
92 */
93 
BM_Downmix(benchmark::State & state)94 static void BM_Downmix(benchmark::State& state) {
95     const audio_channel_mask_t channelMask = kChannelPositionMasks[state.range(0)];
96     const size_t channelCount = audio_channel_count_from_out_mask(channelMask);
97     const int sampleRate = 48000;
98 
99     // Initialize input buffer with deterministic pseudo-random values
100     std::minstd_rand gen(channelMask);
101     std::uniform_real_distribution<> dis(-1.0f, 1.0f);
102     std::vector<float> input(kFrameCount * channelCount);
103     std::vector<float> output(kFrameCount * FCC_2);
104     for (auto& in : input) {
105         in = dis(gen);
106     }
107     effect_handle_t effectHandle = nullptr;
108     if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(
109             &downmix_uuid, 1, 1, &effectHandle);
110         status != 0) {
111         ALOGE("create_effect returned an error = %d\n", status);
112         return;
113     }
114 
115     effect_config_t config{};
116     config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
117     config.inputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
118     config.inputCfg.bufferProvider.getBuffer = nullptr;
119     config.inputCfg.bufferProvider.releaseBuffer = nullptr;
120     config.inputCfg.bufferProvider.cookie = nullptr;
121     config.inputCfg.mask = EFFECT_CONFIG_ALL;
122 
123     config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
124     config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
125     config.outputCfg.bufferProvider.getBuffer = nullptr;
126     config.outputCfg.bufferProvider.releaseBuffer = nullptr;
127     config.outputCfg.bufferProvider.cookie = nullptr;
128     config.outputCfg.mask = EFFECT_CONFIG_ALL;
129 
130     config.inputCfg.samplingRate = sampleRate;
131     config.inputCfg.channels = channelMask;
132 
133     config.outputCfg.samplingRate = sampleRate;
134     config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; // output always stereo
135 
136     int reply = 0;
137     uint32_t replySize = sizeof(reply);
138     if (int status = (*effectHandle)
139             ->command(effectHandle, EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t),
140                     &config, &replySize, &reply);
141         status != 0) {
142         ALOGE("command returned an error = %d\n", status);
143         return;
144     }
145 
146     if (int status = (*effectHandle)
147             ->command(effectHandle, EFFECT_CMD_ENABLE, 0, nullptr, &replySize, &reply);
148         status != 0) {
149         ALOGE("Command enable call returned error %d\n", reply);
150         return;
151     }
152 
153     // Run the test
154     for (auto _ : state) {
155         benchmark::DoNotOptimize(input.data());
156         benchmark::DoNotOptimize(output.data());
157 
158         audio_buffer_t inBuffer = {.frameCount = kFrameCount, .f32 = input.data()};
159         audio_buffer_t outBuffer = {.frameCount = kFrameCount, .f32 = output.data()};
160         (*effectHandle)->process(effectHandle, &inBuffer, &outBuffer);
161 
162         benchmark::ClobberMemory();
163     }
164 
165     state.SetComplexityN(channelCount);
166     state.SetLabel(audio_channel_out_mask_to_string(channelMask));
167 
168     if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(effectHandle); status != 0) {
169         ALOGE("release_effect returned an error = %d\n", status);
170         return;
171     }
172 }
173 
DownmixArgs(benchmark::internal::Benchmark * b)174 static void DownmixArgs(benchmark::internal::Benchmark* b) {
175     for (int i = 0; i < (int)std::size(kChannelPositionMasks); i++) {
176         b->Args({i});
177     }
178 }
179 
180 BENCHMARK(BM_Downmix)->Apply(DownmixArgs);
181 
182 BENCHMARK_MAIN();
183