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